refactor(device_management): migrate do_not_disturb to Riverpod + add delete button
This commit is contained in:
@@ -1,184 +1,200 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:legacy_theme/legacy_theme.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_controller.dart';
|
||||
import 'package:device_management/src/features/do_not_disturb/presentation/providers/do_not_disturb_editor_provider.dart';
|
||||
import 'package:device_management/src/features/do_not_disturb/presentation/providers/do_not_disturb_provider.dart';
|
||||
import 'package:device_management/src/features/do_not_disturb/presentation/widgets/do_not_disturb_period_card.dart';
|
||||
import 'package:device_management/src/features/do_not_disturb/presentation/widgets/edit_period_sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_device_state/legacy_device_state.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:legacy_ui/legacy_ui.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
import '../domain/do_not_disturb_period.dart';
|
||||
import 'state/do_not_disturb_view_model.dart';
|
||||
import 'state/do_not_disturb_view_state.dart';
|
||||
import 'widgets/do_not_disturb_period_card.dart';
|
||||
import 'widgets/edit_period_sheet.dart';
|
||||
|
||||
class DoNotDisturbScreen extends ConsumerWidget {
|
||||
const DoNotDisturbScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(doNotDisturbViewModelProvider.notifier);
|
||||
final (periods, maxPeriods, isLoading, isSaving) = ref.watch(
|
||||
doNotDisturbViewModelProvider.select(
|
||||
(s) => (s.periods, s.maxPeriods, s.isLoading, s.isSaving),
|
||||
),
|
||||
);
|
||||
|
||||
ref.listen(doNotDisturbViewModelProvider.select((s) => s.errorEvent), (
|
||||
_,
|
||||
next,
|
||||
) {
|
||||
if (next == null) return;
|
||||
final message = switch (next) {
|
||||
DoNotDisturbErrorEvent.load || DoNotDisturbErrorEvent.save => context.translate(
|
||||
I18n.doNotDisturbError,
|
||||
),
|
||||
DoNotDisturbErrorEvent.maxPeriods => context
|
||||
.translate(I18n.doNotDisturbMaxPeriods)
|
||||
.replaceAll('{max}', maxPeriods.toString()),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
vm.clearError();
|
||||
});
|
||||
|
||||
ref.listen(doNotDisturbViewModelProvider.select((s) => s.saveSuccess), (
|
||||
_,
|
||||
success,
|
||||
) {
|
||||
if (success) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.doNotDisturbSaved),
|
||||
type: MessageType.success,
|
||||
);
|
||||
vm.clearSaveSuccess();
|
||||
}
|
||||
});
|
||||
|
||||
final primaryColor = context.sfColors.legacyPrimary;
|
||||
final device = ref.watch(selectedDeviceProvider).value;
|
||||
|
||||
ref.listen(doNotDisturbControllerProvider, (prev, next) async {
|
||||
if (prev == null || !prev.isLoading || next.isLoading) return;
|
||||
if (next.hasError) {
|
||||
await showErrorDialog(context, I18n.doNotDisturbError);
|
||||
return;
|
||||
}
|
||||
ref.read(doNotDisturbEditorProvider.notifier).clear();
|
||||
await showSuccessDialog(context, I18n.doNotDisturbSaved);
|
||||
});
|
||||
|
||||
if (device == null) {
|
||||
return LegacyPageLayout(
|
||||
title: context.translate(I18n.doNotDisturb),
|
||||
body: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
final identificator = device.identificator;
|
||||
final maxPeriods = device.capabilities?.doNotDisturbs?.maxPeriods ?? 4;
|
||||
final periodsAsync =
|
||||
ref.watch(doNotDisturbPeriodsProvider(identificator));
|
||||
final editorState = ref.watch(doNotDisturbEditorProvider);
|
||||
final isSaving =
|
||||
ref.watch(doNotDisturbControllerProvider.select((s) => s.isLoading));
|
||||
|
||||
return LegacyPageLayout(
|
||||
title: context.translate(I18n.doNotDisturb),
|
||||
body: isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
vertical: SizeUtils.getByScreen(small: 12, big: 10),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.doNotDisturbDescription),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 13, big: 12),
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
.withValues(alpha: 0.5),
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
),
|
||||
...periods.asMap().entries.map((entry) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: SizeUtils.getByScreen(small: 8, big: 6),
|
||||
),
|
||||
child: DoNotDisturbPeriodCard(
|
||||
period: entry.value,
|
||||
onEdit: () => _editPeriod(
|
||||
context,
|
||||
vm,
|
||||
entry.key,
|
||||
entry.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (periods.length < maxPeriods)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: SizeUtils.getByScreen(small: 8, big: 6),
|
||||
),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _addPeriod(context, vm),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: periodsAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (_, __) => Center(
|
||||
child: Text(context.translate(I18n.doNotDisturbError)),
|
||||
),
|
||||
data: (serverPeriods) {
|
||||
final periods = editorState ?? serverPeriods;
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
vertical: SizeUtils.getByScreen(small: 12, big: 10),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.doNotDisturbDescription),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 13, big: 12),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withValues(alpha: 0.5),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: isSaving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: PrimaryButton(
|
||||
onPressed: () async {
|
||||
child: PrimaryButton(
|
||||
onPressed: isSaving
|
||||
? null
|
||||
: () async {
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
vm.save();
|
||||
if (!context.mounted) return;
|
||||
final serverPeriods =
|
||||
ref.read(doNotDisturbPeriodsProvider(identificator));
|
||||
final periods = ref.read(doNotDisturbEditorProvider) ??
|
||||
serverPeriods.value ??
|
||||
const [];
|
||||
ref.read(doNotDisturbControllerProvider.notifier).save(
|
||||
deviceIdentificator: identificator,
|
||||
periods: periods,
|
||||
);
|
||||
},
|
||||
text: context.translate(I18n.doNotDisturbSave),
|
||||
color: primaryColor,
|
||||
),
|
||||
text: context.translate(I18n.doNotDisturbSave),
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _addPeriod(
|
||||
BuildContext context,
|
||||
DoNotDisturbViewModel vm,
|
||||
WidgetRef ref,
|
||||
List<DoNotDisturbPeriod> current,
|
||||
) async {
|
||||
final period = await showModalBottomSheet<DoNotDisturbPeriod>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (_) => const EditPeriodSheet(),
|
||||
);
|
||||
if (period != null) vm.addPeriod(period);
|
||||
if (period != null) {
|
||||
ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.add(current: current, period: period);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _editPeriod(
|
||||
BuildContext context,
|
||||
DoNotDisturbViewModel vm,
|
||||
WidgetRef ref,
|
||||
List<DoNotDisturbPeriod> current,
|
||||
int index,
|
||||
DoNotDisturbPeriod existing,
|
||||
) async {
|
||||
@@ -187,6 +203,43 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
isScrollControlled: true,
|
||||
builder: (_) => EditPeriodSheet(initial: existing),
|
||||
);
|
||||
if (period != null) vm.updatePeriod(index, period);
|
||||
if (period != null) {
|
||||
ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.replace(current: current, index: index, period: period);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deletePeriod(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
List<DoNotDisturbPeriod> current,
|
||||
int index,
|
||||
) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(context.translate(I18n.doNotDisturbDeletePeriod)),
|
||||
content:
|
||||
Text(context.translate(I18n.doNotDisturbDeletePeriodConfirm)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: Text(context.translate(I18n.cancel)),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
child: Text(context.translate(I18n.delete)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed != true) return;
|
||||
ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.remove(current: current, index: index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
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';
|
||||
|
||||
part 'do_not_disturb_controller.g.dart';
|
||||
|
||||
@riverpod
|
||||
class DoNotDisturbController extends _$DoNotDisturbController {
|
||||
@override
|
||||
FutureOr<void> build() {}
|
||||
|
||||
Future<void> save({
|
||||
required String deviceIdentificator,
|
||||
required List<DoNotDisturbPeriod> periods,
|
||||
}) async {
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
final schedule = await ref
|
||||
.read(doNotDisturbRemoteDatasourceProvider)
|
||||
.updatePeriods(
|
||||
identificator: deviceIdentificator,
|
||||
periods: periods,
|
||||
);
|
||||
ref.invalidate(doNotDisturbPeriodsProvider(deviceIdentificator));
|
||||
unawaited(
|
||||
ref
|
||||
.read(sfTrackingProvider)
|
||||
.legacyDeviceDoNotDisturbScheduleSaved(schedule.periods.length),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'do_not_disturb_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(DoNotDisturbController)
|
||||
const doNotDisturbControllerProvider = DoNotDisturbControllerProvider._();
|
||||
|
||||
final class DoNotDisturbControllerProvider
|
||||
extends $AsyncNotifierProvider<DoNotDisturbController, void> {
|
||||
const DoNotDisturbControllerProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'doNotDisturbControllerProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$doNotDisturbControllerHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
DoNotDisturbController create() => DoNotDisturbController();
|
||||
}
|
||||
|
||||
String _$doNotDisturbControllerHash() =>
|
||||
r'f84df0b051b53a5fadd8801412d45e870aee6637';
|
||||
|
||||
abstract class _$DoNotDisturbController extends $AsyncNotifier<void> {
|
||||
FutureOr<void> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
build();
|
||||
final ref = this.ref as $Ref<AsyncValue<void>, void>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<void>, void>,
|
||||
AsyncValue<void>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:device_management/src/features/do_not_disturb/domain/do_not_disturb_period.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'do_not_disturb_editor_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class DoNotDisturbEditor extends _$DoNotDisturbEditor {
|
||||
@override
|
||||
List<DoNotDisturbPeriod>? build() => null;
|
||||
|
||||
void add({
|
||||
required List<DoNotDisturbPeriod> current,
|
||||
required DoNotDisturbPeriod period,
|
||||
}) {
|
||||
final base = state ?? current;
|
||||
state = [...base, period];
|
||||
}
|
||||
|
||||
void replace({
|
||||
required List<DoNotDisturbPeriod> current,
|
||||
required int index,
|
||||
required DoNotDisturbPeriod period,
|
||||
}) {
|
||||
final base = state ?? current;
|
||||
if (index < 0 || index >= base.length) return;
|
||||
final updated = [...base];
|
||||
updated[index] = period;
|
||||
state = updated;
|
||||
}
|
||||
|
||||
void remove({
|
||||
required List<DoNotDisturbPeriod> current,
|
||||
required int index,
|
||||
}) {
|
||||
final base = state ?? current;
|
||||
if (index < 0 || index >= base.length) return;
|
||||
state = [...base]..removeAt(index);
|
||||
}
|
||||
|
||||
void clear() => state = null;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'do_not_disturb_editor_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(DoNotDisturbEditor)
|
||||
const doNotDisturbEditorProvider = DoNotDisturbEditorProvider._();
|
||||
|
||||
final class DoNotDisturbEditorProvider
|
||||
extends $NotifierProvider<DoNotDisturbEditor, List<DoNotDisturbPeriod>?> {
|
||||
const DoNotDisturbEditorProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'doNotDisturbEditorProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$doNotDisturbEditorHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
DoNotDisturbEditor create() => DoNotDisturbEditor();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(List<DoNotDisturbPeriod>? value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<List<DoNotDisturbPeriod>?>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$doNotDisturbEditorHash() =>
|
||||
r'a82e3bc2ca0eaf5d185d5e7624d2956acf883269';
|
||||
|
||||
abstract class _$DoNotDisturbEditor
|
||||
extends $Notifier<List<DoNotDisturbPeriod>?> {
|
||||
List<DoNotDisturbPeriod>? build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref =
|
||||
this.ref as $Ref<List<DoNotDisturbPeriod>?, List<DoNotDisturbPeriod>?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<List<DoNotDisturbPeriod>?, List<DoNotDisturbPeriod>?>,
|
||||
List<DoNotDisturbPeriod>?,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
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:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'do_not_disturb_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<DoNotDisturbPeriod>> doNotDisturbPeriods(
|
||||
Ref ref,
|
||||
String deviceIdentificator,
|
||||
) async {
|
||||
final schedule = await ref
|
||||
.read(doNotDisturbRemoteDatasourceProvider)
|
||||
.getSchedule(identificator: deviceIdentificator);
|
||||
return schedule.periods;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'do_not_disturb_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(doNotDisturbPeriods)
|
||||
const doNotDisturbPeriodsProvider = DoNotDisturbPeriodsFamily._();
|
||||
|
||||
final class DoNotDisturbPeriodsProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<DoNotDisturbPeriod>>,
|
||||
List<DoNotDisturbPeriod>,
|
||||
FutureOr<List<DoNotDisturbPeriod>>
|
||||
>
|
||||
with
|
||||
$FutureModifier<List<DoNotDisturbPeriod>>,
|
||||
$FutureProvider<List<DoNotDisturbPeriod>> {
|
||||
const DoNotDisturbPeriodsProvider._({
|
||||
required DoNotDisturbPeriodsFamily super.from,
|
||||
required String super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'doNotDisturbPeriodsProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$doNotDisturbPeriodsHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'doNotDisturbPeriodsProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<List<DoNotDisturbPeriod>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<List<DoNotDisturbPeriod>> create(Ref ref) {
|
||||
final argument = this.argument as String;
|
||||
return doNotDisturbPeriods(ref, argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is DoNotDisturbPeriodsProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$doNotDisturbPeriodsHash() =>
|
||||
r'da5bacd8516e788f19a356f9c9fac81c351b698d';
|
||||
|
||||
final class DoNotDisturbPeriodsFamily extends $Family
|
||||
with $FunctionalFamilyOverride<FutureOr<List<DoNotDisturbPeriod>>, String> {
|
||||
const DoNotDisturbPeriodsFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'doNotDisturbPeriodsProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
DoNotDisturbPeriodsProvider call(String deviceIdentificator) =>
|
||||
DoNotDisturbPeriodsProvider._(argument: deviceIdentificator, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'doNotDisturbPeriodsProvider';
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
import '../../../../core/data/datasources/do_not_disturb_remote_datasource.dart';
|
||||
import '../../domain/do_not_disturb_period.dart';
|
||||
import '../../../../core/providers/do_not_disturb_providers.dart';
|
||||
import 'do_not_disturb_view_state.dart';
|
||||
|
||||
final doNotDisturbViewModelProvider =
|
||||
NotifierProvider.autoDispose<
|
||||
DoNotDisturbViewModel,
|
||||
DoNotDisturbViewState
|
||||
>(DoNotDisturbViewModel.new);
|
||||
|
||||
class DoNotDisturbViewModel extends Notifier<DoNotDisturbViewState> {
|
||||
late final DoNotDisturbRemoteDatasource _datasource;
|
||||
late final SfTrackingRepository _tracking;
|
||||
|
||||
@override
|
||||
DoNotDisturbViewState build() {
|
||||
_datasource = ref.read(doNotDisturbRemoteDatasourceProvider);
|
||||
_tracking = ref.read(sfTrackingProvider);
|
||||
Future.microtask(_load);
|
||||
return const DoNotDisturbViewState();
|
||||
}
|
||||
|
||||
String? get _identificator =>
|
||||
ref.read(selectedDeviceProvider).value?.identificator;
|
||||
|
||||
Future<void> _load() async {
|
||||
final identificator = _identificator;
|
||||
if (identificator == null) {
|
||||
state = state.copyWith(isLoading: false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final schedule = await _datasource.getSchedule(
|
||||
identificator: identificator,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
final device = ref.read(selectedDeviceProvider).value;
|
||||
final capabilities = device?.capabilities;
|
||||
final maxPeriods = capabilities?.doNotDisturbs?.maxPeriods ?? 4;
|
||||
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
periods: schedule.periods,
|
||||
maxPeriods: maxPeriods,
|
||||
);
|
||||
} catch (_) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorEvent: DoNotDisturbErrorEvent.load,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void addPeriod(DoNotDisturbPeriod period) {
|
||||
if (state.periods.length >= state.maxPeriods) {
|
||||
state = state.copyWith(errorEvent: DoNotDisturbErrorEvent.maxPeriods);
|
||||
return;
|
||||
}
|
||||
state = state.copyWith(
|
||||
periods: [...state.periods, period],
|
||||
errorEvent: null,
|
||||
);
|
||||
}
|
||||
|
||||
void updatePeriod(int index, DoNotDisturbPeriod period) {
|
||||
if (index < 0 || index >= state.periods.length) return;
|
||||
final updated = [...state.periods];
|
||||
updated[index] = period;
|
||||
state = state.copyWith(periods: updated, errorEvent: null);
|
||||
}
|
||||
|
||||
void removePeriod(int index) {
|
||||
if (index < 0 || index >= state.periods.length) return;
|
||||
final updated = [...state.periods]..removeAt(index);
|
||||
state = state.copyWith(periods: updated, errorEvent: null);
|
||||
}
|
||||
|
||||
void clearError() {
|
||||
if (state.errorEvent != null) state = state.copyWith(errorEvent: null);
|
||||
}
|
||||
|
||||
void clearSaveSuccess() {
|
||||
if (state.saveSuccess) state = state.copyWith(saveSuccess: false);
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
final identificator = _identificator;
|
||||
if (identificator == null) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: true,
|
||||
saveSuccess: false,
|
||||
errorEvent: null,
|
||||
);
|
||||
|
||||
try {
|
||||
final schedule = await _datasource.updatePeriods(
|
||||
identificator: identificator,
|
||||
periods: state.periods,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
periods: schedule.periods,
|
||||
saveSuccess: true,
|
||||
);
|
||||
|
||||
unawaited(
|
||||
_tracking.legacyDeviceDoNotDisturbScheduleSaved(
|
||||
schedule.periods.length,
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorEvent: DoNotDisturbErrorEvent.save,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../domain/do_not_disturb_period.dart';
|
||||
|
||||
part 'do_not_disturb_view_state.freezed.dart';
|
||||
|
||||
enum DoNotDisturbErrorEvent { load, save, maxPeriods }
|
||||
|
||||
@freezed
|
||||
abstract class DoNotDisturbViewState with _$DoNotDisturbViewState {
|
||||
const factory DoNotDisturbViewState({
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isSaving,
|
||||
@Default([]) List<DoNotDisturbPeriod> periods,
|
||||
@Default(4) int maxPeriods,
|
||||
DoNotDisturbErrorEvent? errorEvent,
|
||||
@Default(false) bool saveSuccess,
|
||||
}) = _DoNotDisturbViewState;
|
||||
}
|
||||
@@ -1,292 +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 'do_not_disturb_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$DoNotDisturbViewState {
|
||||
|
||||
bool get isLoading; bool get isSaving; List<DoNotDisturbPeriod> get periods; int get maxPeriods; DoNotDisturbErrorEvent? get errorEvent; bool get saveSuccess;
|
||||
/// Create a copy of DoNotDisturbViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$DoNotDisturbViewStateCopyWith<DoNotDisturbViewState> get copyWith => _$DoNotDisturbViewStateCopyWithImpl<DoNotDisturbViewState>(this as DoNotDisturbViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DoNotDisturbViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&const DeepCollectionEquality().equals(other.periods, periods)&&(identical(other.maxPeriods, maxPeriods) || other.maxPeriods == maxPeriods)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isLoading,isSaving,const DeepCollectionEquality().hash(periods),maxPeriods,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DoNotDisturbViewState(isLoading: $isLoading, isSaving: $isSaving, periods: $periods, maxPeriods: $maxPeriods, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $DoNotDisturbViewStateCopyWith<$Res> {
|
||||
factory $DoNotDisturbViewStateCopyWith(DoNotDisturbViewState value, $Res Function(DoNotDisturbViewState) _then) = _$DoNotDisturbViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool isLoading, bool isSaving, List<DoNotDisturbPeriod> periods, int maxPeriods, DoNotDisturbErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$DoNotDisturbViewStateCopyWithImpl<$Res>
|
||||
implements $DoNotDisturbViewStateCopyWith<$Res> {
|
||||
_$DoNotDisturbViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final DoNotDisturbViewState _self;
|
||||
final $Res Function(DoNotDisturbViewState) _then;
|
||||
|
||||
/// Create a copy of DoNotDisturbViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isSaving = null,Object? periods = null,Object? maxPeriods = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
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,periods: null == periods ? _self.periods : periods // ignore: cast_nullable_to_non_nullable
|
||||
as List<DoNotDisturbPeriod>,maxPeriods: null == maxPeriods ? _self.maxPeriods : maxPeriods // ignore: cast_nullable_to_non_nullable
|
||||
as int,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as DoNotDisturbErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [DoNotDisturbViewState].
|
||||
extension DoNotDisturbViewStatePatterns on DoNotDisturbViewState {
|
||||
/// 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( _DoNotDisturbViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbViewState() 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( _DoNotDisturbViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbViewState():
|
||||
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( _DoNotDisturbViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbViewState() 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( bool isLoading, bool isSaving, List<DoNotDisturbPeriod> periods, int maxPeriods, DoNotDisturbErrorEvent? errorEvent, bool saveSuccess)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbViewState() when $default != null:
|
||||
return $default(_that.isLoading,_that.isSaving,_that.periods,_that.maxPeriods,_that.errorEvent,_that.saveSuccess);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( bool isLoading, bool isSaving, List<DoNotDisturbPeriod> periods, int maxPeriods, DoNotDisturbErrorEvent? errorEvent, bool saveSuccess) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbViewState():
|
||||
return $default(_that.isLoading,_that.isSaving,_that.periods,_that.maxPeriods,_that.errorEvent,_that.saveSuccess);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( bool isLoading, bool isSaving, List<DoNotDisturbPeriod> periods, int maxPeriods, DoNotDisturbErrorEvent? errorEvent, bool saveSuccess)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbViewState() when $default != null:
|
||||
return $default(_that.isLoading,_that.isSaving,_that.periods,_that.maxPeriods,_that.errorEvent,_that.saveSuccess);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _DoNotDisturbViewState implements DoNotDisturbViewState {
|
||||
const _DoNotDisturbViewState({this.isLoading = true, this.isSaving = false, final List<DoNotDisturbPeriod> periods = const [], this.maxPeriods = 4, this.errorEvent, this.saveSuccess = false}): _periods = periods;
|
||||
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isSaving;
|
||||
final List<DoNotDisturbPeriod> _periods;
|
||||
@override@JsonKey() List<DoNotDisturbPeriod> get periods {
|
||||
if (_periods is EqualUnmodifiableListView) return _periods;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_periods);
|
||||
}
|
||||
|
||||
@override@JsonKey() final int maxPeriods;
|
||||
@override final DoNotDisturbErrorEvent? errorEvent;
|
||||
@override@JsonKey() final bool saveSuccess;
|
||||
|
||||
/// Create a copy of DoNotDisturbViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$DoNotDisturbViewStateCopyWith<_DoNotDisturbViewState> get copyWith => __$DoNotDisturbViewStateCopyWithImpl<_DoNotDisturbViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DoNotDisturbViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&const DeepCollectionEquality().equals(other._periods, _periods)&&(identical(other.maxPeriods, maxPeriods) || other.maxPeriods == maxPeriods)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isLoading,isSaving,const DeepCollectionEquality().hash(_periods),maxPeriods,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DoNotDisturbViewState(isLoading: $isLoading, isSaving: $isSaving, periods: $periods, maxPeriods: $maxPeriods, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$DoNotDisturbViewStateCopyWith<$Res> implements $DoNotDisturbViewStateCopyWith<$Res> {
|
||||
factory _$DoNotDisturbViewStateCopyWith(_DoNotDisturbViewState value, $Res Function(_DoNotDisturbViewState) _then) = __$DoNotDisturbViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool isLoading, bool isSaving, List<DoNotDisturbPeriod> periods, int maxPeriods, DoNotDisturbErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$DoNotDisturbViewStateCopyWithImpl<$Res>
|
||||
implements _$DoNotDisturbViewStateCopyWith<$Res> {
|
||||
__$DoNotDisturbViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _DoNotDisturbViewState _self;
|
||||
final $Res Function(_DoNotDisturbViewState) _then;
|
||||
|
||||
/// Create a copy of DoNotDisturbViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isSaving = null,Object? periods = null,Object? maxPeriods = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_DoNotDisturbViewState(
|
||||
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,periods: null == periods ? _self._periods : periods // ignore: cast_nullable_to_non_nullable
|
||||
as List<DoNotDisturbPeriod>,maxPeriods: null == maxPeriods ? _self.maxPeriods : maxPeriods // ignore: cast_nullable_to_non_nullable
|
||||
as int,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as DoNotDisturbErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -9,11 +9,13 @@ import 'week_day_row.dart';
|
||||
class DoNotDisturbPeriodCard extends ConsumerWidget {
|
||||
final DoNotDisturbPeriod period;
|
||||
final VoidCallback onEdit;
|
||||
final VoidCallback onDelete;
|
||||
|
||||
const DoNotDisturbPeriodCard({
|
||||
super.key,
|
||||
required this.period,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -57,6 +59,15 @@ class DoNotDisturbPeriodCard extends ConsumerWidget {
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import 'package:device_management/src/core/data/datasources/do_not_disturb_remote_datasource.dart';
|
||||
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_controller.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
import 'package:sf_shared/testing.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
class MockDoNotDisturbRemoteDatasource extends Mock
|
||||
implements DoNotDisturbRemoteDatasource {}
|
||||
|
||||
const _period = DoNotDisturbPeriod(
|
||||
start: '22:00',
|
||||
end: '07:00',
|
||||
week: '1111100',
|
||||
);
|
||||
|
||||
void main() {
|
||||
setUpAll(() {
|
||||
registerFallbackValue(const <DoNotDisturbPeriod>[]);
|
||||
});
|
||||
|
||||
ProviderContainer buildContainer(DoNotDisturbRemoteDatasource ds) {
|
||||
return makeContainer(
|
||||
overrides: [
|
||||
doNotDisturbRemoteDatasourceProvider.overrideWithValue(ds),
|
||||
sfTrackingProvider.overrideWithValue(
|
||||
SfTrackingRepository(clients: const []),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
group('DoNotDisturbController.save', () {
|
||||
test('persists periods and transitions to AsyncData', () async {
|
||||
final ds = MockDoNotDisturbRemoteDatasource();
|
||||
when(
|
||||
() => ds.updatePeriods(
|
||||
identificator: any(named: 'identificator'),
|
||||
periods: any(named: 'periods'),
|
||||
),
|
||||
).thenAnswer((_) async => const DoNotDisturbSchedule(
|
||||
id: 's1',
|
||||
deviceIdentificator: 'imei-1',
|
||||
periods: [_period],
|
||||
));
|
||||
|
||||
final container = buildContainer(ds);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
await container.read(doNotDisturbControllerProvider.notifier).save(
|
||||
deviceIdentificator: 'imei-1',
|
||||
periods: const [_period],
|
||||
);
|
||||
|
||||
expect(
|
||||
container.read(doNotDisturbControllerProvider),
|
||||
isA<AsyncData<void>>(),
|
||||
);
|
||||
verify(
|
||||
() => ds.updatePeriods(
|
||||
identificator: 'imei-1',
|
||||
periods: const [_period],
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('exposes AsyncError when datasource fails', () async {
|
||||
final ds = MockDoNotDisturbRemoteDatasource();
|
||||
when(
|
||||
() => ds.updatePeriods(
|
||||
identificator: any(named: 'identificator'),
|
||||
periods: any(named: 'periods'),
|
||||
),
|
||||
).thenThrow(const ApiException(message: 'boom', isNetworkError: true));
|
||||
|
||||
final container = buildContainer(ds);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
await container.read(doNotDisturbControllerProvider.notifier).save(
|
||||
deviceIdentificator: 'imei-1',
|
||||
periods: const [_period],
|
||||
);
|
||||
|
||||
expect(
|
||||
container.read(doNotDisturbControllerProvider),
|
||||
isA<AsyncError<void>>(),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -185,7 +185,7 @@ class _ContactFormSheetWrapperState
|
||||
);
|
||||
}
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (!mounted) return;
|
||||
final state = ref.read(blockPhoneControllerProvider);
|
||||
if (state.hasError) return;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -130,7 +130,7 @@ class _AddSosContactSheetState extends ConsumerState<_AddSosContactSheet> {
|
||||
),
|
||||
);
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (!mounted) return;
|
||||
final state = ref.read(sosContactsControllerProvider);
|
||||
if (state.hasError) return;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -863,6 +863,8 @@
|
||||
"doNotDisturbMaxPeriods": "Maximal {max} Zeiträume erlaubt",
|
||||
"doNotDisturbSave": "Änderungen speichern",
|
||||
"doNotDisturbSaved": "Zeiträume gespeichert",
|
||||
"doNotDisturbDeletePeriod": "Zeitraum löschen",
|
||||
"doNotDisturbDeletePeriodConfirm": "Möchten Sie diesen Zeitraum wirklich löschen? Die Änderung wird beim Speichern angewendet.",
|
||||
"doNotDisturbConfirm": "Bestätigen",
|
||||
"doNotDisturbStart": "Beginn",
|
||||
"doNotDisturbEnd": "Ende",
|
||||
|
||||
@@ -574,6 +574,8 @@
|
||||
"doNotDisturbMaxPeriods": "Maximum {max} periods allowed",
|
||||
"doNotDisturbSave": "Save changes",
|
||||
"doNotDisturbSaved": "Periods saved successfully",
|
||||
"doNotDisturbDeletePeriod": "Delete period",
|
||||
"doNotDisturbDeletePeriodConfirm": "Are you sure you want to delete this period? The change is applied on save.",
|
||||
"doNotDisturbConfirm": "Confirm",
|
||||
"doNotDisturbStart": "Start",
|
||||
"doNotDisturbEnd": "End",
|
||||
|
||||
@@ -575,6 +575,8 @@
|
||||
"doNotDisturbMaxPeriods": "Máximo {max} periodos permitidos",
|
||||
"doNotDisturbSave": "Guardar cambios",
|
||||
"doNotDisturbSaved": "Periodos guardados correctamente",
|
||||
"doNotDisturbDeletePeriod": "Eliminar período",
|
||||
"doNotDisturbDeletePeriodConfirm": "¿Seguro que quieres eliminar este período? El cambio se aplica al guardar.",
|
||||
"doNotDisturbConfirm": "Confirmar",
|
||||
"doNotDisturbStart": "Inicio",
|
||||
"doNotDisturbEnd": "Fin",
|
||||
|
||||
@@ -863,6 +863,8 @@
|
||||
"doNotDisturbMaxPeriods": "Maximum {max} périodes autorisées",
|
||||
"doNotDisturbSave": "Enregistrer",
|
||||
"doNotDisturbSaved": "Périodes enregistrées",
|
||||
"doNotDisturbDeletePeriod": "Supprimer la période",
|
||||
"doNotDisturbDeletePeriodConfirm": "Voulez-vous supprimer cette période ? Le changement s'applique lors de l'enregistrement.",
|
||||
"doNotDisturbConfirm": "Confirmer",
|
||||
"doNotDisturbStart": "Début",
|
||||
"doNotDisturbEnd": "Fin",
|
||||
|
||||
@@ -863,6 +863,8 @@
|
||||
"doNotDisturbMaxPeriods": "Massimo {max} periodi consentiti",
|
||||
"doNotDisturbSave": "Salva modifiche",
|
||||
"doNotDisturbSaved": "Periodi salvati",
|
||||
"doNotDisturbDeletePeriod": "Elimina periodo",
|
||||
"doNotDisturbDeletePeriodConfirm": "Vuoi eliminare questo periodo? La modifica si applica al salvataggio.",
|
||||
"doNotDisturbConfirm": "Conferma",
|
||||
"doNotDisturbStart": "Inizio",
|
||||
"doNotDisturbEnd": "Fine",
|
||||
|
||||
@@ -863,6 +863,8 @@
|
||||
"doNotDisturbMaxPeriods": "Máximo {max} períodos permitidos",
|
||||
"doNotDisturbSave": "Guardar alterações",
|
||||
"doNotDisturbSaved": "Períodos guardados",
|
||||
"doNotDisturbDeletePeriod": "Eliminar período",
|
||||
"doNotDisturbDeletePeriodConfirm": "Tens a certeza que queres eliminar este período? A alteração aplica-se ao guardar.",
|
||||
"doNotDisturbConfirm": "Confirmar",
|
||||
"doNotDisturbStart": "Início",
|
||||
"doNotDisturbEnd": "Fim",
|
||||
|
||||
@@ -332,6 +332,9 @@ class I18n {
|
||||
static const String doNotDisturbMaxPeriods = 'doNotDisturbMaxPeriods';
|
||||
static const String doNotDisturbSave = 'doNotDisturbSave';
|
||||
static const String doNotDisturbSaved = 'doNotDisturbSaved';
|
||||
static const String doNotDisturbDeletePeriod = 'doNotDisturbDeletePeriod';
|
||||
static const String doNotDisturbDeletePeriodConfirm =
|
||||
'doNotDisturbDeletePeriodConfirm';
|
||||
static const String doNotDisturbStart = 'doNotDisturbStart';
|
||||
static const String doNotDisturbStatusOff = 'doNotDisturbStatusOff';
|
||||
static const String doNotDisturbStatusOn = 'doNotDisturbStatusOn';
|
||||
|
||||
Reference in New Issue
Block a user