Skip to content

Commit

Permalink
#138 allow multi select on trigger page
Browse files Browse the repository at this point in the history
  • Loading branch information
Codel1417 committed May 16, 2024
1 parent 13fe804 commit 4a56f9b
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 47 deletions.
36 changes: 20 additions & 16 deletions lib/Backend/sensors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import 'move_lists.dart';
part 'sensors.g.dart';

final sensorsLogger = log.Logger('Sensors');
final _random = Random();

@HiveType(typeId: 2)
class Trigger extends ChangeNotifier {
Expand Down Expand Up @@ -181,21 +182,24 @@ abstract class TriggerDefinition extends ChangeNotifier implements Comparable<Tr
// 15 second cooldown between moves
return;
}
BaseAction? baseAction = ref.read(getActionFromUUIDProvider(triggerAction.action));
if (baseAction == null) {
return;
}
triggerAction.isActive.value = true;
Map<String, BaseStatefulDevice> knownDevices = ref.read(knownDevicesProvider);
List<BaseStatefulDevice> devices = knownDevices.values.where((BaseStatefulDevice element) => deviceTypes.values.flattened.toSet().contains(element.baseDeviceDefinition.deviceType)).where((element) => element.deviceState.value == DeviceState.standby).toList();
for (BaseStatefulDevice baseStatefulDevice in List.of(devices)..shuffle()) {
if (SentryHive.box(settings).get(kitsuneModeToggle, defaultValue: kitsuneModeDefault)) {
await Future.delayed(Duration(milliseconds: Random().nextInt(kitsuneDelayRange)));
if (triggerAction.actions.isNotEmpty) {
String action = triggerAction.actions[_random.nextInt(triggerAction.actions.length)];
BaseAction? baseAction = ref.read(getActionFromUUIDProvider(action));
if (baseAction == null) {
return;
}
triggerAction.isActive.value = true;
Map<String, BaseStatefulDevice> knownDevices = ref.read(knownDevicesProvider);
List<BaseStatefulDevice> devices = knownDevices.values.where((BaseStatefulDevice element) => deviceTypes.values.flattened.toSet().contains(element.baseDeviceDefinition.deviceType)).where((element) => element.deviceState.value == DeviceState.standby).toList();
for (BaseStatefulDevice baseStatefulDevice in List.of(devices)..shuffle()) {
if (SentryHive.box(settings).get(kitsuneModeToggle, defaultValue: kitsuneModeDefault)) {
await Future.delayed(Duration(milliseconds: Random().nextInt(kitsuneDelayRange)));
}
runAction(baseAction, baseStatefulDevice);
}
runAction(baseAction, baseStatefulDevice);
await Future.delayed(const Duration(seconds: 15));
triggerAction.isActive.value = false;
}
await Future.delayed(const Duration(seconds: 15));
triggerAction.isActive.value = false;
},
);
}
Expand Down Expand Up @@ -597,7 +601,7 @@ class TriggerAction {
@HiveField(1)
String uuid; //uuid matches triggerActionDef
@HiveField(2)
String? action;
List<String> actions = [];
ValueNotifier<bool> isActive = ValueNotifier(false);

TriggerAction(this.uuid);
Expand All @@ -618,8 +622,8 @@ class TriggerList extends _$TriggerList {
if (SentryHive.box(settings).get(firstLaunchSensors, defaultValue: firstLaunchSensorsDefault)) {
TriggerDefinition triggerDefinition = ref.read(triggerDefinitionListProvider).where((element) => element.uuid == 'ee9379e2-ec4f-40bb-8674-fd223a6edfda').first;
Trigger trigger = Trigger.trigDef(triggerDefinition, '91e3d421-6a52-45ab-a23e-f38e4987a8f5');
trigger.actions.firstWhere((element) => element.uuid == '77d22961-5a69-465a-bd27-5cf5508d10a6').action = ActionRegistry.allCommands.firstWhere((element) => element.uuid == 'c53e980e-899e-4148-a13e-f57a8f9707f4').uuid;
trigger.actions.firstWhere((element) => element.uuid == '7424097d-ba24-4d85-b963-bf58e85e289d').action = ActionRegistry.allCommands.firstWhere((element) => element.uuid == '86b13d13-b09c-46ba-a887-b40d8118b00a').uuid;
trigger.actions.firstWhere((element) => element.uuid == '77d22961-5a69-465a-bd27-5cf5508d10a6').actions.add(ActionRegistry.allCommands.firstWhere((element) => element.uuid == 'c53e980e-899e-4148-a13e-f57a8f9707f4').uuid);
trigger.actions.firstWhere((element) => element.uuid == '7424097d-ba24-4d85-b963-bf58e85e289d').actions.add(ActionRegistry.allCommands.firstWhere((element) => element.uuid == '86b13d13-b09c-46ba-a887-b40d8118b00a').uuid);
SentryHive.box(settings).put(firstLaunchSensors, false);
SentryHive.box<Trigger>(triggerBox)
..clear()
Expand Down
2 changes: 1 addition & 1 deletion lib/Frontend/go_router_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ final GoRouter router = GoRouter(
parentNavigatorKey: _rootNavigatorKey,
pageBuilder: (BuildContext context, GoRouterState state) => MaterialPage(
child: ActionSelector(
deviceType: state.extra! as Set<DeviceType>,
actionSelectorInfo: state.extra! as ActionSelectorInfo,
),
key: state.pageKey,
name: 'Triggers/Select Action',
Expand Down
4 changes: 3 additions & 1 deletion lib/Frontend/intn_defs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ String sequencesPageDescription() => Intl.message('Create custom Actions for you
// Triggers Page
String triggersSelectLabel() => Intl.message('Select a Trigger Type', name: 'triggersSelectLabel', desc: 'The title of the add trigger dialog');

String triggersSelectClearLabel() => Intl.message('Remove Action', name: 'triggersSelectClearLabel', desc: 'The button label on the trigger select screen for clearing the selected action');
String triggersSelectClearLabel() => Intl.message('Remove Actions', name: 'triggersSelectClearLabel', desc: 'The button label on the trigger select screen for clearing the selected actions');

String triggersSelectSaveLabel() => Intl.message('Save Actions', name: 'triggersSelectSaveLabel', desc: 'The button label on the trigger select screen for saving the selected actions');

String ok() => Intl.message('Ok', name: 'ok', desc: 'Ok on dialog boxes');

Expand Down
87 changes: 63 additions & 24 deletions lib/Frontend/pages/action_selector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,41 @@ import 'package:back_button_interceptor/back_button_interceptor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:tail_app/constants.dart';

import '../../Backend/Definitions/Action/base_action.dart';
import '../../Backend/Definitions/Device/device_definition.dart';
import '../../Backend/action_registry.dart';
import '../intn_defs.dart';

class ActionSelectorInfo {
final Set<DeviceType> deviceType;
final List<BaseAction> selectedActions;

ActionSelectorInfo({required this.deviceType, required this.selectedActions});
}

class ActionSelector extends ConsumerStatefulWidget {
const ActionSelector({super.key, required this.deviceType});
const ActionSelector({super.key, required this.actionSelectorInfo});

final Set<DeviceType> deviceType;
final ActionSelectorInfo actionSelectorInfo;

@override
ConsumerState<ActionSelector> createState() => _ActionSelectorState();
}

class _ActionSelectorState extends ConsumerState<ActionSelector> {
Map<ActionCategory, Set<BaseAction>> actionsCatMap = {};
List<ActionCategory> catList = [];
List<BaseAction> selected = [];

@override
void initState() {
super.initState();
BackButtonInterceptor.add(myInterceptor);
actionsCatMap = ref.read(getAllActionsProvider(widget.actionSelectorInfo.deviceType));
catList = actionsCatMap.keys.toList();
selected = widget.actionSelectorInfo.selectedActions;
}

@override
Expand All @@ -37,13 +52,16 @@ class _ActionSelectorState extends ConsumerState<ActionSelector> {

@override
Widget build(BuildContext context) {
Map<ActionCategory, Set<BaseAction>> actionsCatMap = ref.read(getAllActionsProvider(widget.deviceType));
List<ActionCategory> catList = actionsCatMap.keys.toList();
return Scaffold(
primary: true,
appBar: AppBar(
title: Text(actionsSelectScreen()),
actions: [
IconButton(
onPressed: () => context.pop(selected),
icon: const Icon(Icons.save),
tooltip: triggersSelectSaveLabel(),
),
IconButton(
onPressed: () => context.pop(true),
icon: const Icon(Icons.clear),
Expand All @@ -65,31 +83,52 @@ class _ActionSelectorState extends ConsumerState<ActionSelector> {
),
),
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 125),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: actionsForCat.length,
itemBuilder: (BuildContext context, int actionIndex) {
return Card(
clipBehavior: Clip.antiAlias,
elevation: 2,
child: InkWell(
onTap: () => Navigator.pop(context, actionsForCat[actionIndex]),
child: SizedBox(
height: 50,
width: 50,
child: Center(
child: Text(actionsForCat[actionIndex].name, semanticsLabel: actionsForCat[actionIndex].name, overflow: TextOverflow.fade, textAlign: TextAlign.center),
),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 125),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: actionsForCat.length,
itemBuilder: (BuildContext context, int actionIndex) {
BaseAction baseAction = actionsForCat[actionIndex];
bool isSelected = selected.contains(baseAction);
return TweenAnimationBuilder(
builder: (context, value, child) => Card(
clipBehavior: Clip.antiAlias,
elevation: 2,
color: Color.lerp(Theme.of(context).cardColor, Theme.of(context).primaryColor, value),
child: child,
),
),
);
},
)
tween: isSelected ? Tween<double>(begin: 0, end: 1) : Tween<double>(begin: 1, end: 0),
duration: animationTransitionDuration,
child: cardChild(isSelected, baseAction),
);
})
],
);
},
),
);
}

InkWell cardChild(bool isSelected, BaseAction baseAction) {
return InkWell(
onTap: () {
if (isSelected) {
setState(() {
selected.remove(baseAction);
});
} else {
setState(() {
selected.add(baseAction);
});
}
},
child: SizedBox(
height: 50,
width: 50,
child: Center(
child: Text(baseAction.name, semanticsLabel: baseAction.name, overflow: TextOverflow.fade, textAlign: TextAlign.center),
),
),
);
}
}
26 changes: 21 additions & 5 deletions lib/Frontend/pages/triggers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,19 @@ class _TriggerEditState extends ConsumerState<TriggerEdit> {
return AnimatedCrossFade(
duration: animationTransitionDuration,
secondChild: const LinearProgressIndicator(),
firstChild: Text(ref.watch(getActionFromUUIDProvider(e.action))?.name ?? triggerActionNotSet()),
firstChild: Builder(builder: (context) {
String text = "";
for (String actionUUID in e.actions) {
BaseAction? baseAction = ref.watch(getActionFromUUIDProvider(actionUUID));
if (baseAction != null) {
if (text.isNotEmpty) {
text += ', ';
}
text += baseAction.name;
}
}
return Text(text.isNotEmpty ? text : triggerActionNotSet());
}),
crossFadeState: !value ? CrossFadeState.showFirst : CrossFadeState.showSecond,
);
},
Expand All @@ -239,21 +251,25 @@ class _TriggerEditState extends ConsumerState<TriggerEdit> {
barrierColor: Theme.of(context).canvasColor,
context: context,
builder: (BuildContext context) {
return Dialog.fullscreen(backgroundColor: Theme.of(context).canvasColor, child: ActionSelector(deviceType: widget.trigger.deviceType.toSet()));
return Dialog.fullscreen(
backgroundColor: Theme.of(context).canvasColor,
child: ActionSelector(
actionSelectorInfo: ActionSelectorInfo(deviceType: widget.trigger.deviceType.toSet(), selectedActions: []),
));
},
);
if (result is BaseAction) {
if (result is List<BaseAction>) {
setState(
() {
e.action = result.uuid;
e.actions = result.map((element) => element.uuid).toList();
ref.watch(triggerListProvider.notifier).store();
},
);
} else if (result is bool) {
if (!result) {
setState(
() {
e.action = null;
e.actions = [];
ref.watch(triggerListProvider.notifier).store();
},
);
Expand Down

0 comments on commit 4a56f9b

Please sign in to comment.