mirror of
https://github.com/chenasraf/mudblock.git
synced 2026-05-17 17:48:05 +00:00
feat: unified automation list page
This commit is contained in:
@@ -13,17 +13,20 @@ enum MUDActionTarget {
|
||||
output,
|
||||
immediate,
|
||||
none,
|
||||
variable,
|
||||
}
|
||||
|
||||
class MUDAction {
|
||||
String content;
|
||||
Automation? parent;
|
||||
MUDActionTarget target;
|
||||
List<String> args;
|
||||
|
||||
MUDAction(
|
||||
this.content, {
|
||||
this.target = MUDActionTarget.execute,
|
||||
this.parent,
|
||||
this.args = const [],
|
||||
});
|
||||
|
||||
void invoke(GameStore store, List<String> matches) {
|
||||
@@ -78,6 +81,10 @@ class MUDAction {
|
||||
debugPrint('ActionSendTo.immediate: $content');
|
||||
store.send(content);
|
||||
break;
|
||||
case MUDActionTarget.variable:
|
||||
debugPrint('ActionSendTo.variable: $content');
|
||||
store.saveVariable(args.single, content);
|
||||
break;
|
||||
case MUDActionTarget.none:
|
||||
debugPrint('ActionSendTo.none: $content');
|
||||
break;
|
||||
|
||||
@@ -39,23 +39,23 @@ class Alias extends Automation {
|
||||
);
|
||||
|
||||
static AliasProcessResult processLine(
|
||||
GameStore store, List<Alias> triggers, String line) {
|
||||
GameStore store, List<Alias> aliases, String line) {
|
||||
final res = AliasProcessResult();
|
||||
final str = ColorUtils.stripColor(line);
|
||||
for (final trigger in triggers) {
|
||||
if (!trigger.isAvailable) {
|
||||
for (final alias in aliases) {
|
||||
if (!alias.isAvailable) {
|
||||
continue;
|
||||
}
|
||||
if (trigger.matches(str)) {
|
||||
if (alias.matches(str)) {
|
||||
res.processed = true;
|
||||
trigger.invokeEffect(store, str);
|
||||
if (trigger.isRemovedFromBuffer ||
|
||||
alias.invokeEffect(store, str);
|
||||
if (alias.isRemovedFromBuffer ||
|
||||
[MUDActionTarget.script, MUDActionTarget.input]
|
||||
.contains(trigger.action.target)) {
|
||||
.contains(alias.action.target)) {
|
||||
res.lineRemoved = true;
|
||||
}
|
||||
if (trigger.autoDisable) {
|
||||
trigger.tempDisabled = true;
|
||||
if (alias.autoDisable) {
|
||||
alias.tempDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class Automation {
|
||||
/// This is used to temporarily disable an automation after using it once when it has autoDisable set to true.
|
||||
bool tempDisabled = false;
|
||||
MUDAction action;
|
||||
String get displayName => label.isNotEmpty ? label : pattern;
|
||||
|
||||
Automation({
|
||||
this.enabled = true,
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'action.dart';
|
||||
import 'alias.dart';
|
||||
import 'game_button_set.dart';
|
||||
import 'trigger.dart';
|
||||
import 'variable.dart';
|
||||
|
||||
class LuaInterpreter {
|
||||
LuaState state = LuaState.newState();
|
||||
@@ -99,13 +98,7 @@ class LuaBindings {
|
||||
ls.pop(2);
|
||||
debugPrint("lua.setVariable $name, $value");
|
||||
final profile = store.currentProfile;
|
||||
if (profile.variables[name] == null) {
|
||||
profile.variables[name] = Variable(name, value);
|
||||
}
|
||||
profile.variables[name]!.value = value;
|
||||
profile.saveVariable(
|
||||
profile.variables[name]!,
|
||||
);
|
||||
profile.saveVariable(name, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,8 +145,9 @@ class PluginBase extends ChangeNotifier {
|
||||
return storage.deleteFile('button_sets/${buttonSet.id}');
|
||||
}
|
||||
|
||||
Future<void> saveVariable(Variable update) async {
|
||||
debugPrint('$this saveVariable: $update');
|
||||
Future<void> saveVariable(String name, String value) async {
|
||||
debugPrint('$this saveVariable: $name, $value');
|
||||
final update = Variable(name, value);
|
||||
variables[update.name] = update;
|
||||
notifyListeners();
|
||||
return storage.writeFile(
|
||||
|
||||
@@ -192,6 +192,7 @@ class MUDProfile extends PluginBase {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum AuthMethod {
|
||||
|
||||
@@ -110,7 +110,7 @@ final routes = <String, Widget Function(BuildContext)>{
|
||||
builder: (context, store, child) => VariableListPage(
|
||||
variables: store.currentProfile.variables.values.toList(),
|
||||
onSave: (variable) async {
|
||||
store.currentProfile.saveVariable(variable);
|
||||
store.currentProfile.saveVariable(variable.name, variable.value);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -12,7 +12,6 @@ import 'package:provider/provider.dart';
|
||||
import '../core/features/settings.dart';
|
||||
import '../core/profile_presets.dart';
|
||||
import '../core/storage.dart';
|
||||
import '../core/string_utils.dart';
|
||||
import 'consts.dart';
|
||||
import 'features/action.dart';
|
||||
import 'features/alias.dart';
|
||||
@@ -166,7 +165,11 @@ class GameStore extends ChangeNotifier {
|
||||
if (color != null && !line.startsWith('$esc[')) {
|
||||
line = '$esc[${color}m$line';
|
||||
}
|
||||
onLine(line, newLine: i != lines.length - 1);
|
||||
onLine(
|
||||
line,
|
||||
// newLine: i != lines.length - 1);
|
||||
newLine: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +253,7 @@ class GameStore extends ChangeNotifier {
|
||||
|
||||
void onLine(String line, {bool newLine = false}) {
|
||||
final result = Trigger.processLine(this, triggers, line);
|
||||
debugPrint('Processed line: $line');
|
||||
if (!result.lineRemoved) {
|
||||
echo(line, newLine: newLine);
|
||||
}
|
||||
@@ -499,6 +503,10 @@ class GameStore extends ChangeNotifier {
|
||||
static GameStore of(BuildContext context) {
|
||||
return Provider.of<GameStore>(context, listen: false);
|
||||
}
|
||||
|
||||
void saveVariable(String name, String value) {
|
||||
currentProfile.saveVariable(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
mixin GameStoreMixin {
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:mudblock/core/store.dart';
|
||||
|
||||
import '../core/features/alias.dart';
|
||||
import '../core/routes.dart';
|
||||
import 'generic_list_page.dart';
|
||||
import 'automation_list_page.dart';
|
||||
|
||||
class AliasListPage extends StatelessWidget with GameStoreMixin {
|
||||
const AliasListPage({
|
||||
@@ -19,59 +19,13 @@ class AliasListPage extends StatelessWidget with GameStoreMixin {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GenericListPage(
|
||||
title: const Text('Aliases'),
|
||||
save: onSave,
|
||||
items: storeOf(context).currentProfile.aliases,
|
||||
detailsPath: Paths.alias,
|
||||
displayName: (alias) => alias.pattern,
|
||||
searchTags: (alias) => [
|
||||
alias.action.content,
|
||||
alias.group,
|
||||
],
|
||||
itemBuilder: (context, alias) {
|
||||
return ListTile(
|
||||
key: Key(alias.id),
|
||||
title: Text(alias.pattern),
|
||||
subtitle: Text(alias.action.content.replaceAll('\n', ' ')),
|
||||
leading: Switch.adaptive(
|
||||
value: alias.enabled,
|
||||
onChanged: (value) {
|
||||
alias.enabled = value;
|
||||
onSave(alias);
|
||||
},
|
||||
),
|
||||
trailing: PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
const PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text('Delete'),
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
switch (value) {
|
||||
case 'delete':
|
||||
onDelete(alias);
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
onTap: () async {
|
||||
final updated = await Navigator.pushNamed(
|
||||
context,
|
||||
Paths.alias,
|
||||
arguments: alias,
|
||||
);
|
||||
if (updated != null) {
|
||||
await onSave(updated as Alias);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
return AutomationListPage<Alias>(
|
||||
automations: aliases,
|
||||
onSave: onSave,
|
||||
onDelete: onDelete,
|
||||
detailsPagePath: Paths.alias,
|
||||
title: 'Aliases',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
81
lib/pages/automation_list_page.dart
Normal file
81
lib/pages/automation_list_page.dart
Normal file
@@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mudblock/core/store.dart';
|
||||
|
||||
import '../core/features/automation.dart';
|
||||
import 'generic_list_page.dart';
|
||||
|
||||
class AutomationListPage<T extends Automation> extends StatelessWidget
|
||||
with GameStoreMixin {
|
||||
const AutomationListPage({
|
||||
super.key,
|
||||
required this.automations,
|
||||
required this.onSave,
|
||||
required this.onDelete,
|
||||
required this.detailsPagePath,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
final List<T> automations;
|
||||
final Future<void> Function(T) onSave;
|
||||
final Future<void> Function(T) onDelete;
|
||||
final String detailsPagePath;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GenericListPage(
|
||||
title: Text(title),
|
||||
save: onSave,
|
||||
items: automations,
|
||||
detailsPath: detailsPagePath,
|
||||
displayName: (automation) => automation.displayName,
|
||||
searchTags: (automation) => [
|
||||
automation.action.content,
|
||||
automation.group,
|
||||
],
|
||||
itemBuilder: (context, automation) {
|
||||
return ListTile(
|
||||
key: Key(automation.id),
|
||||
title: Text(automation.displayName),
|
||||
subtitle: Text(automation.action.content.replaceAll('\n', '↵')),
|
||||
leading: Switch.adaptive(
|
||||
value: automation.enabled,
|
||||
onChanged: (value) {
|
||||
automation.enabled = value;
|
||||
onSave(automation);
|
||||
},
|
||||
),
|
||||
isThreeLine: true,
|
||||
trailing: PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
const PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text('Delete'),
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
switch (value) {
|
||||
case 'delete':
|
||||
onDelete(automation);
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
onTap: () async {
|
||||
final updated = await Navigator.pushNamed(
|
||||
context,
|
||||
detailsPagePath,
|
||||
arguments: automation,
|
||||
);
|
||||
if (updated != null) {
|
||||
await onSave(updated as T);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,13 @@ class AutomationPage<T extends Automation> extends StatefulWidget {
|
||||
class _AutomationPageState<T extends Automation>
|
||||
extends State<AutomationPage<T>> {
|
||||
late final T automation;
|
||||
final variableTargetController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
automation = widget.create(widget.automation);
|
||||
variableTargetController.text =
|
||||
automation.action.args.isNotEmpty ? automation.action.args.single : '';
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@@ -139,14 +142,12 @@ class _AutomationPageState<T extends Automation>
|
||||
initialSelection: automation.action.target,
|
||||
onSelected: (value) {
|
||||
if (value is MUDActionTarget) {
|
||||
automation.action.target = value;
|
||||
setState(() {
|
||||
automation.action.target = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
dropdownMenuEntries: const [
|
||||
DropdownMenuEntry(
|
||||
value: MUDActionTarget.world,
|
||||
label: 'World',
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: MUDActionTarget.execute,
|
||||
label: 'Execute',
|
||||
@@ -155,6 +156,14 @@ class _AutomationPageState<T extends Automation>
|
||||
value: MUDActionTarget.script,
|
||||
label: 'Script',
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: MUDActionTarget.variable,
|
||||
label: 'Variable',
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: MUDActionTarget.world,
|
||||
label: 'World',
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: MUDActionTarget.input,
|
||||
label: 'Command Input',
|
||||
@@ -169,6 +178,18 @@ class _AutomationPageState<T extends Automation>
|
||||
),
|
||||
],
|
||||
),
|
||||
if (automation.action.target == MUDActionTarget.variable)
|
||||
TextField(
|
||||
controller: variableTargetController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Variable name',
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
helperText: 'The name of the variable to set',
|
||||
),
|
||||
onChanged: (value) {
|
||||
automation.action.args = [value];
|
||||
},
|
||||
),
|
||||
CheckboxListTile(
|
||||
title: const Text('Case Sensitive'),
|
||||
subtitle: const Text(
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:mudblock/core/store.dart';
|
||||
|
||||
import '../core/features/trigger.dart';
|
||||
import '../core/routes.dart';
|
||||
import 'generic_list_page.dart';
|
||||
import 'automation_list_page.dart';
|
||||
|
||||
class TriggerListPage extends StatelessWidget with GameStoreMixin {
|
||||
const TriggerListPage({
|
||||
@@ -19,58 +19,12 @@ class TriggerListPage extends StatelessWidget with GameStoreMixin {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GenericListPage(
|
||||
title: const Text('Triggers'),
|
||||
save: onSave,
|
||||
items: storeOf(context).currentProfile.triggers,
|
||||
detailsPath: Paths.trigger,
|
||||
displayName: (trigger) => trigger.pattern,
|
||||
searchTags: (trigger) => [
|
||||
trigger.action.content,
|
||||
trigger.group,
|
||||
],
|
||||
itemBuilder: (context, trigger) {
|
||||
return ListTile(
|
||||
key: Key(trigger.id),
|
||||
title: Text(trigger.pattern),
|
||||
subtitle: Text(trigger.action.content.replaceAll('\n', ' ')),
|
||||
leading: Switch.adaptive(
|
||||
value: trigger.enabled,
|
||||
onChanged: (value) {
|
||||
trigger.enabled = value;
|
||||
onSave(trigger);
|
||||
},
|
||||
),
|
||||
isThreeLine: true,
|
||||
trailing: PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
const PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text('Delete'),
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
switch (value) {
|
||||
case 'delete':
|
||||
onDelete(trigger);
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
onTap: () async {
|
||||
final updated = await Navigator.pushNamed(
|
||||
context,
|
||||
Paths.trigger,
|
||||
arguments: trigger,
|
||||
);
|
||||
if (updated != null) {
|
||||
await onSave(updated as Trigger);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
return AutomationListPage<Trigger>(
|
||||
automations: triggers,
|
||||
onSave: onSave,
|
||||
onDelete: onDelete,
|
||||
detailsPagePath: Paths.trigger,
|
||||
title: 'Triggers',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user