feat(legacy): block device commands when watch is disconnected
This commit is contained in:
@@ -38,6 +38,7 @@ class PedometerToggle extends ConsumerWidget {
|
||||
value: enabled,
|
||||
activeTrackColor: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
onChanged: (value) async {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final success = await ref
|
||||
.read(activityMeterViewModelProvider.notifier)
|
||||
.togglePedometer(enabled: value);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
@@ -94,7 +95,12 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: state.isSaving ? null : vm.uploadPhoto,
|
||||
onPressed: state.isSaving
|
||||
? null
|
||||
: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.uploadPhoto();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.add_photo_alternate_outlined,
|
||||
color: Colors.white,
|
||||
@@ -112,10 +118,19 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
: state.isSaving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state.photos.isEmpty
|
||||
? _EmptyState(onUpload: vm.uploadPhoto, primaryColor: primaryColor)
|
||||
? _EmptyState(
|
||||
onUpload: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.uploadPhoto();
|
||||
},
|
||||
primaryColor: primaryColor,
|
||||
)
|
||||
: _PhotoGrid(
|
||||
state: state,
|
||||
onPhotoTap: vm.setAsBackground,
|
||||
onPhotoTap: (id) {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.setAsBackground(id);
|
||||
},
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -59,6 +59,7 @@ class ContactsScreen extends ConsumerWidget {
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
if (state.contacts.length >= state.maxContacts) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
|
||||
@@ -155,7 +155,10 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
child: isSaving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: PrimaryButton(
|
||||
onPressed: vm.save,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.save();
|
||||
},
|
||||
text: context.translate(I18n.doNotDisturbSave),
|
||||
color: primaryColor,
|
||||
),
|
||||
|
||||
@@ -109,6 +109,7 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
|
||||
options: device.capabilities!.heartbeats!.options,
|
||||
theme: theme,
|
||||
onChanged: (frequency) async {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final success = await vm.updateHeartRateFrequency(
|
||||
frequency: frequency,
|
||||
);
|
||||
@@ -195,7 +196,12 @@ class _SaveSection extends ConsumerWidget {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: isMeasuring ? null : () => vm.measure(),
|
||||
onPressed: isMeasuring
|
||||
? null
|
||||
: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.measure();
|
||||
},
|
||||
text: isMeasuring ? '...' : context.translate(I18n.measure),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
|
||||
import '../state/locate_device_view_model.dart';
|
||||
|
||||
class LocateDeviceDialog extends ConsumerWidget {
|
||||
@@ -88,7 +90,10 @@ class LocateDeviceDialog extends ConsumerWidget {
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 8, big: 16)),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: vm.locateDevice,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.locateDevice();
|
||||
},
|
||||
text: context.translate(I18n.accept),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
height: SizeUtils.getByScreen(small: 38, big: 36),
|
||||
|
||||
@@ -152,7 +152,10 @@ class _TakePictureSection extends ConsumerWidget {
|
||||
big: EdgeInsets.symmetric(vertical: 10, horizontal: 25),
|
||||
),
|
||||
child: PrimaryButton(
|
||||
onPressed: vm.takePicture,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.takePicture();
|
||||
},
|
||||
text: context.translate(I18n.takePicture),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
height: SizeUtils.getByScreen(small: 36, big: 35),
|
||||
|
||||
@@ -33,6 +33,7 @@ class RemoteConnectionScreen extends ConsumerWidget {
|
||||
if (cameraEnabled) ...[
|
||||
_SectionButton(
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
@@ -49,6 +50,7 @@ class RemoteConnectionScreen extends ConsumerWidget {
|
||||
],
|
||||
_SectionButton(
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(child: SpyCallDialog()),
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
@@ -76,9 +77,15 @@ class SpyCallDialog extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
|
||||
_PhoneSection(onSubmit: vm.call),
|
||||
_PhoneSection(onSubmit: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.call();
|
||||
}),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
|
||||
_CallSection(onPressed: vm.call),
|
||||
_CallSection(onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.call();
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -104,8 +104,10 @@ class _ScheduledActivitiesScreenState
|
||||
shape: const CircleBorder(),
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: () =>
|
||||
showActivityFormSheet(context, weekDay: _selectedWeekDay),
|
||||
onTap: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showActivityFormSheet(context, weekDay: _selectedWeekDay);
|
||||
},
|
||||
child: SizedBox(
|
||||
width: SizeUtils.getByScreen(small: 48, big: 46),
|
||||
height: SizeUtils.getByScreen(small: 48, big: 46),
|
||||
|
||||
@@ -99,6 +99,7 @@ class _ActivityFormSheetState extends ConsumerState<ActivityFormSheet> {
|
||||
|
||||
void _submit() {
|
||||
if (!_isFormValid) return;
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
|
||||
final name = _nameController.text.trim();
|
||||
final period = _buildPeriod();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
@@ -155,14 +156,14 @@ class _TimelineDotAndLine extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ActivityTimelineCard extends StatelessWidget {
|
||||
class _ActivityTimelineCard extends ConsumerWidget {
|
||||
final ScheduledActivityEntity activity;
|
||||
final ThemePort theme;
|
||||
|
||||
const _ActivityTimelineCard({required this.activity, required this.theme});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(bottom: SizeUtils.getByScreen(small: 10, big: 8)),
|
||||
padding: SizeUtils.getByScreen(
|
||||
@@ -187,7 +188,10 @@ class _ActivityTimelineCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => showActivityFormSheet(context, activity: activity),
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showActivityFormSheet(context, activity: activity);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.edit_outlined,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
@@ -198,6 +202,7 @@ class _ActivityTimelineCard extends StatelessWidget {
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => Dialog(
|
||||
|
||||
@@ -81,7 +81,10 @@ class VolumeControlScreen extends ConsumerWidget {
|
||||
child: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: PrimaryButton(
|
||||
onPressed: vm.submit,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.submit();
|
||||
},
|
||||
text: context.translate(I18n.volumeSend),
|
||||
color: primaryColor,
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:location/src/core/domain/entities/frequent_place_entity.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
@@ -74,6 +75,7 @@ class _FrequentPlaceSheetState extends ConsumerState<_FrequentPlaceSheet> {
|
||||
|
||||
Future<void> _submit() async {
|
||||
if (!_canSave) return;
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
|
||||
final vm = ref.read(locationViewModelProvider.notifier);
|
||||
final bool success;
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:location/src/core/domain/entities/geofence_entity.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
@@ -90,6 +91,7 @@ class _GeofenceSheetState extends ConsumerState<_GeofenceSheet> {
|
||||
|
||||
Future<void> _submit() async {
|
||||
if (!_canSave) return;
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
|
||||
final vm = ref.read(locationViewModelProvider.notifier);
|
||||
final description = _descriptionController.text.trim();
|
||||
|
||||
@@ -91,6 +91,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
_followTimer?.cancel();
|
||||
final frequency = widget.selectedDevice?.settings.frequency ?? 60;
|
||||
_followTimer = Timer.periodic(Duration(seconds: frequency), (_) {
|
||||
if (ref.read(selectedDeviceProvider).value?.isDisconnected ?? true) return;
|
||||
widget.onRefreshPosition();
|
||||
});
|
||||
}
|
||||
@@ -236,6 +237,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
}
|
||||
|
||||
Future<void> _updateFrequency(int frequency) async {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final success = await ref
|
||||
.read(locationViewModelProvider.notifier)
|
||||
.updateLocationFrequency(frequency: frequency);
|
||||
@@ -263,6 +265,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
}
|
||||
|
||||
void _confirmPlacement() {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final center = _mapController.camera.center;
|
||||
final mapState = ref.read(locationMapViewModelProvider);
|
||||
|
||||
@@ -287,6 +290,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
}
|
||||
|
||||
void _confirmRadius() {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final mapState = ref.read(locationMapViewModelProvider);
|
||||
final point = mapState.previewPoint!;
|
||||
final radius = mapState.previewRadius;
|
||||
@@ -411,6 +415,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
}
|
||||
|
||||
void _onEditFrequentPlace(FrequentPlaceEntity fp) {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
_vm.clearSelectedFrequentPlace();
|
||||
showNameInputSheet(
|
||||
context,
|
||||
@@ -669,6 +674,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
onAddFrequentPlace: () => _vm.startPlacing(PlacingMode.frequentPlace),
|
||||
onShareTap: _shareLocation,
|
||||
onRefreshTap: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
unawaited(
|
||||
ref.read(sfTrackingProvider).legacyLocationMapRefreshTapped(),
|
||||
);
|
||||
@@ -682,6 +688,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
onCenterTap: _centerOnDevice,
|
||||
onToggleFollow: () {
|
||||
final willActivate = !mapState.isFollowing;
|
||||
if (willActivate && !guardDeviceCommand(context, ref)) return;
|
||||
_vm.toggleFollowing();
|
||||
unawaited(
|
||||
ref
|
||||
@@ -714,8 +721,12 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
child: GeofenceInfoCard(
|
||||
geofence: mapState.selectedGeofence!,
|
||||
onClose: _vm.clearSelectedGeofence,
|
||||
onEdit: () => _vm.startEditingGeofence(mapState.selectedGeofence!),
|
||||
onEdit: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
_vm.startEditingGeofence(mapState.selectedGeofence!);
|
||||
},
|
||||
onDelete: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final id = mapState.selectedGeofence!.id;
|
||||
_vm.clearSelectedGeofence();
|
||||
ref
|
||||
@@ -732,6 +743,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
onClose: _vm.clearSelectedFrequentPlace,
|
||||
onEdit: () => _onEditFrequentPlace(mapState.selectedFrequentPlace!),
|
||||
onDelete: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final id = mapState.selectedFrequentPlace!.id;
|
||||
_vm.clearSelectedFrequentPlace();
|
||||
ref
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
@@ -91,7 +92,10 @@ class AlarmScreen extends ConsumerWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () => _openForm(context, vm),
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
_openForm(context, vm);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
@@ -122,7 +126,10 @@ class AlarmScreen extends ConsumerWidget {
|
||||
child: isSaving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: PrimaryButton(
|
||||
onPressed: vm.save,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.save();
|
||||
},
|
||||
text: context.translate(I18n.alarmSave),
|
||||
color: primaryColor,
|
||||
),
|
||||
|
||||
@@ -79,7 +79,12 @@ class AlertsScreen extends ConsumerWidget {
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: state.isSaving ? null : () => vm.save(),
|
||||
onPressed: state.isSaving
|
||||
? null
|
||||
: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.save();
|
||||
},
|
||||
text: state.isSaving ? '...' : context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
|
||||
@@ -99,7 +99,10 @@ class BatteryScreen extends ConsumerWidget {
|
||||
activeTrackColor: primaryColor,
|
||||
onChanged: state.isSaving
|
||||
? null
|
||||
: (value) => vm.toggleNightMode(value),
|
||||
: (value) {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.toggleNightMode(value);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
@@ -83,7 +84,10 @@ class BlockPhoneScreen extends ConsumerWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () => showAddContactSheet(context),
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showAddContactSheet(context);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
@@ -218,6 +222,7 @@ class _ContactList extends ConsumerWidget {
|
||||
int index,
|
||||
String name,
|
||||
) {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final theme = ref.read(themePortProvider);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
|
||||
@@ -76,7 +76,12 @@ class DisableFunctionsScreen extends ConsumerWidget {
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: state.isSaving ? null : () => vm.save(),
|
||||
onPressed: state.isSaving
|
||||
? null
|
||||
: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.save();
|
||||
},
|
||||
text: state.isSaving ? '...' : context.translate(I18n.save),
|
||||
color: primaryColor,
|
||||
),
|
||||
|
||||
@@ -110,7 +110,10 @@ class _SaveSection extends ConsumerWidget {
|
||||
child: isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: PrimaryButton(
|
||||
onPressed: vm.submit,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.submit();
|
||||
},
|
||||
text: context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
|
||||
@@ -52,6 +52,7 @@ class _OptionsSection extends ConsumerWidget {
|
||||
subtitle: context.translate(I18n.remoteTurnOffMessage),
|
||||
icon: Icons.settings_power_outlined,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
@@ -95,6 +96,7 @@ class _OptionsSection extends ConsumerWidget {
|
||||
subtitle: context.translate(I18n.remoteRestartMessage),
|
||||
icon: Icons.refresh_outlined,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
@@ -116,6 +118,7 @@ class _OptionsSection extends ConsumerWidget {
|
||||
subtitle: context.translate(I18n.remoteFactoryResetMessage),
|
||||
icon: Icons.restart_alt_outlined,
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
|
||||
@@ -178,6 +178,7 @@ class _SaveSection extends ConsumerWidget {
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.submit();
|
||||
},
|
||||
text: context.translate(I18n.save),
|
||||
|
||||
@@ -94,7 +94,12 @@ class TimezoneScreen extends ConsumerWidget {
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: state.isSaving ? null : () => vm.save(),
|
||||
onPressed: state.isSaving
|
||||
? null
|
||||
: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
vm.save();
|
||||
},
|
||||
text: state.isSaving ? '...' : context.translate(I18n.save),
|
||||
color: primaryColor,
|
||||
),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
@@ -83,7 +84,10 @@ class WifiSettingsScreen extends ConsumerWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () => showAddWifiNetworkSheet(context),
|
||||
onPressed: () {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
showAddWifiNetworkSheet(context);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
@@ -212,6 +216,7 @@ class _NetworkList extends ConsumerWidget {
|
||||
}
|
||||
|
||||
void _confirmDelete(BuildContext context, WidgetRef ref, dynamic network) {
|
||||
if (!guardDeviceCommand(context, ref)) return;
|
||||
final theme = ref.read(themePortProvider);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
|
||||
@@ -17,3 +17,4 @@ export 'src/providers/devices_repository_provider.dart';
|
||||
export 'src/providers/legacy_devices_provider.dart';
|
||||
export 'src/data/datasources/device_settings_update_datasource.dart';
|
||||
export 'src/providers/device_settings_update_provider.dart';
|
||||
export 'src/utils/device_command_guard.dart';
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
import '../providers/selected_device_provider.dart';
|
||||
|
||||
bool guardDeviceCommand(BuildContext context, WidgetRef ref) {
|
||||
final device = ref.read(selectedDeviceProvider).value;
|
||||
if (device == null || device.isDisconnected) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.errorDeviceDisconnected),
|
||||
type: MessageType.error,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -674,6 +674,7 @@
|
||||
"errorContactsMin": "Das Gerät muss mindestens einen Kontakt haben",
|
||||
"errorContactsMax": "Maximale Kontaktanzahl für dieses Gerät erreicht",
|
||||
"errorPositions": "Positionen konnten nicht geladen werden",
|
||||
"errorDeviceDisconnected": "Die Uhr ist getrennt und kann keine Befehle empfangen",
|
||||
"errorSosContactsMax": "Maximale SOS-Kontaktanzahl für dieses Gerät erreicht",
|
||||
"customBackground": "Benutzerdefiniertes Hintergrundbild",
|
||||
"backgroundImageDescription": "Legen Sie ein Foto als benutzerdefinierten Bildschirmschoner für das Gerät fest",
|
||||
|
||||
@@ -826,6 +826,7 @@
|
||||
"errorContactsMin": "The device must have at least one contact",
|
||||
"errorContactsMax": "Maximum contacts reached for this device",
|
||||
"errorPositions": "Could not load positions",
|
||||
"errorDeviceDisconnected": "The watch is disconnected and cannot receive commands",
|
||||
"errorSosContactsMax": "Maximum SOS contacts reached for this device",
|
||||
"customBackground": "Custom background image",
|
||||
"backgroundImageDescription": "Set a photo as a custom screensaver for the device",
|
||||
|
||||
@@ -827,6 +827,7 @@
|
||||
"errorContactsMin": "El dispositivo debe tener al menos un contacto",
|
||||
"errorContactsMax": "Se ha alcanzado el máximo de contactos para este dispositivo",
|
||||
"errorPositions": "No se pudieron cargar las posiciones",
|
||||
"errorDeviceDisconnected": "El reloj está desconectado y no puede recibir comandos",
|
||||
"errorSosContactsMax": "Se ha alcanzado el máximo de contactos SOS para este dispositivo",
|
||||
"customBackground": "Fondo de pantalla personalizado",
|
||||
"backgroundImageDescription": "Configura una foto como protector de pantalla exclusivo para el dispositivo",
|
||||
|
||||
@@ -674,6 +674,7 @@
|
||||
"errorContactsMin": "L'appareil doit avoir au moins un contact",
|
||||
"errorContactsMax": "Nombre maximum de contacts atteint pour cet appareil",
|
||||
"errorPositions": "Impossible de charger les positions",
|
||||
"errorDeviceDisconnected": "La montre est déconnectée et ne peut pas recevoir de commandes",
|
||||
"errorSosContactsMax": "Nombre maximum de contacts SOS atteint pour cet appareil",
|
||||
"customBackground": "Image de fond personnalisée",
|
||||
"backgroundImageDescription": "Définissez une photo comme écran de veille personnalisé pour l'appareil",
|
||||
|
||||
@@ -674,6 +674,7 @@
|
||||
"errorContactsMin": "Il dispositivo deve avere almeno un contatto",
|
||||
"errorContactsMax": "Numero massimo di contatti raggiunto per questo dispositivo",
|
||||
"errorPositions": "Impossibile caricare le posizioni",
|
||||
"errorDeviceDisconnected": "L'orologio è disconnesso e non può ricevere comandi",
|
||||
"errorSosContactsMax": "Numero massimo di contatti SOS raggiunto per questo dispositivo",
|
||||
"customBackground": "Immagine di sfondo personalizzata",
|
||||
"backgroundImageDescription": "Imposta una foto come screensaver personalizzato per il dispositivo",
|
||||
|
||||
@@ -674,6 +674,7 @@
|
||||
"errorContactsMin": "O dispositivo deve ter pelo menos um contacto",
|
||||
"errorContactsMax": "Número máximo de contactos atingido para este dispositivo",
|
||||
"errorPositions": "Não foi possível carregar as posições",
|
||||
"errorDeviceDisconnected": "O relógio está desconectado e não pode receber comandos",
|
||||
"errorSosContactsMax": "Número máximo de contactos SOS atingido para este dispositivo",
|
||||
"customBackground": "Imagem de fundo personalizada",
|
||||
"backgroundImageDescription": "Defina uma foto como protetor de ecrã personalizado para o dispositivo",
|
||||
|
||||
@@ -378,6 +378,7 @@ class I18n {
|
||||
static const String errorCall = 'errorCall';
|
||||
static const String errorContactsMax = 'errorContactsMax';
|
||||
static const String errorPositions = 'errorPositions';
|
||||
static const String errorDeviceDisconnected = 'errorDeviceDisconnected';
|
||||
static const String errorSosContactsMax = 'errorSosContactsMax';
|
||||
static const String errorContactsMin = 'errorContactsMin';
|
||||
static const String errorDisableFunctions = 'errorDisableFunctions';
|
||||
|
||||
@@ -37,3 +37,7 @@ abstract class DeviceEntity with _$DeviceEntity {
|
||||
String? updatedAt,
|
||||
}) = _DeviceEntity;
|
||||
}
|
||||
|
||||
extension DeviceEntityFlags on DeviceEntity {
|
||||
bool get isDisconnected => flags['isDisconnect'] == true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user