feat: settings page

This commit is contained in:
2023-10-13 02:50:37 +03:00
parent 383f346c55
commit 118b5d8c21
6 changed files with 158 additions and 13 deletions

View File

@@ -8,6 +8,7 @@ import '../storage.dart';
import '../string_utils.dart';
import 'alias.dart';
import 'game_button_set.dart';
import 'settings.dart';
import 'trigger.dart';
import 'variable.dart';
@@ -160,6 +161,15 @@ class MUDProfile {
return KeyboardShortcuts.fromJson(shortcuts);
}
Future<Settings> loadSettings() async {
debugPrint('MUDProfile.loadSettings: $id');
final settings = await ProfileStorage.readProfileFile(id, 'settings');
if (settings == null) {
return Settings.empty();
}
return Settings.fromJson(settings);
}
Future<void> saveAlias(Alias alias) async {
debugPrint('MUDProfile.saveAlias: $id/aliases/${alias.id}');
return ProfileStorage.writeProfileFile(
@@ -194,6 +204,11 @@ class MUDProfile {
id, 'keyboard_shortcuts', shortcuts.toJson());
}
Future<void> saveSettings(Settings settings) async {
debugPrint('MUDProfile.saveSettings: $id');
return ProfileStorage.writeProfileFile(id, 'settings', settings.toJson());
}
Future<void> deleteButtonSet(GameButtonSetData buttonSet) async {
debugPrint('MUDProfile.deleteButtonSet: $id/button_sets/${buttonSet.id}');
return ProfileStorage.deleteProfileFile(id, 'button_sets/${buttonSet.id}');

View File

@@ -0,0 +1,41 @@
class Settings {
String commandSeparator;
bool echoCommands;
bool showTimestamps;
Settings({
required this.commandSeparator,
required this.echoCommands,
required this.showTimestamps,
});
factory Settings.empty() => Settings(
commandSeparator: ';',
echoCommands: true,
showTimestamps: false,
);
factory Settings.fromJson(Map<String, dynamic> json) => Settings(
commandSeparator: json['commandSeparator'] as String,
echoCommands: json['echoCommands'] as bool? ?? true,
showTimestamps: json['showTimestamps'] as bool? ?? false,
);
Map<String, dynamic> toJson() => {
'commandSeparator': commandSeparator,
'echoCommands': echoCommands,
'showTimestamps': showTimestamps,
};
Settings copyWith({
String? commandSeparator,
bool? echoCommands,
}) {
return Settings(
commandSeparator: commandSeparator ?? this.commandSeparator,
echoCommands: echoCommands ?? this.echoCommands,
showTimestamps: showTimestamps,
);
}
}

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:mudblock/pages/keyboard_shortcuts_page.dart';
import '../core/features/alias.dart';
import '../core/features/trigger.dart';
@@ -10,8 +9,10 @@ import '../pages/button_set_page.dart';
import '../pages/button_sets_list_page.dart';
import '../pages/home_page.dart';
import '../pages/home_scaffold.dart';
import '../pages/keyboard_shortcuts_page.dart';
import '../pages/profile_page.dart';
import '../pages/select_profile_page.dart';
import '../pages/settings_page.dart';
import '../pages/trigger_list_page.dart';
import '../pages/trigger_page.dart';
import '../pages/variable_list_page.dart';
@@ -98,6 +99,9 @@ final routes = <String, Widget Function(BuildContext)>{
);
});
},
Paths.settings: (context) {
return const SettingsPage();
},
Paths.home: (context) => HomeScaffold(
builder: (context, _) {
return const HomePage();

View File

@@ -1,4 +0,0 @@
class Settings {
String commandSeparator = ';';
bool echoCommands = true;
}

View File

@@ -15,6 +15,7 @@ import 'features/action.dart';
import 'features/alias.dart';
import 'features/game_button_set.dart';
import 'features/profile.dart';
import 'features/settings.dart';
import 'features/trigger.dart';
import 'features/variable.dart';
import 'keyboard_shortcuts.dart';
@@ -29,9 +30,8 @@ class GameStore extends ChangeNotifier {
final FocusNode inputFocus = FocusNode();
bool isCompressed = false;
final ZLibDecoder decoder = ZLibDecoder();
final msgSplitPattern = RegExp("($cr$lf)|($lf$cr)|$cr|$lf");
final incomingMsgSplitPattern = RegExp("($cr$lf)|($lf$cr)|$cr|$lf");
// accepts csp but NOT double csp
final outgoingMsgSplitPattern = RegExp("(?<!$csp)$csp(?!$csp)");
final ZLibCodec _decoder = ZLibCodec();
final StreamController<List<int>> _rawStreamController = StreamController();
late Stream<List<int>> _decodedStream;
@@ -40,12 +40,13 @@ class GameStore extends ChangeNotifier {
MUDProfile? _currentProfile;
bool _clientReady = false;
// TODO move to settings
/// command separator
static const csp = ";";
String get commandSeparator => settings.commandSeparator;
RegExp get outgoingMsgSplitPattern =>
RegExp("(?<!$commandSeparator)$commandSeparator(?!$commandSeparator)");
// features
// TODO - move to MUDProfile and make that reactive
Settings settings = Settings.empty();
final List<Trigger> triggers = [];
final List<Alias> aliases = [];
final Map<String, Variable> variables = {};
@@ -87,6 +88,8 @@ class GameStore extends ChangeNotifier {
loadVariables(),
loadButtonSets(),
loadKeyboardShortcuts(),
loadSettings(),
]);
_client.connect();
}
@@ -131,6 +134,13 @@ class GameStore extends ChangeNotifier {
debugPrint('KeyboardShortcuts loaded');
}
Future<void> loadSettings() async {
final settings = await currentProfile.loadSettings();
this.settings = settings;
notifyListeners();
debugPrint('Settings loaded');
}
bool processTriggers(String line) {
bool showLine = true;
final str = ColorUtils.stripColor(line);
@@ -203,7 +213,7 @@ class GameStore extends ChangeNotifier {
try {
final data = Message(bytes);
handleMCCPHandshake(data);
for (final line in data.text.split(msgSplitPattern)) {
for (final line in data.text.split(incomingMsgSplitPattern)) {
onLine(line);
}
} catch (e, stack) {
@@ -223,7 +233,7 @@ class GameStore extends ChangeNotifier {
handleMCCPHandshake(data);
}
for (final line in data.text.split(msgSplitPattern)) {
for (final line in data.text.split(incomingMsgSplitPattern)) {
onLine(line);
}
} catch (e, stack) {
@@ -331,7 +341,8 @@ class GameStore extends ChangeNotifier {
List<String> _splitCsp(String line) {
return line
.split(outgoingMsgSplitPattern)
.map((l) => l.replaceAll('$csp$csp', csp))
.map((l) => l.replaceAll(
'$commandSeparator$commandSeparator', commandSeparator))
.toList();
}

View File

@@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
import '../core/features/settings.dart';
import '../core/store.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> with GameStoreStateMixin {
late Settings settings;
@override
void initState() {
super.initState();
debugPrint('SettingsPage.initState');
settings = store.settings.copyWith();
}
@override
Widget build(BuildContext context) {
return GameStore.consumer(
builder: (context, store, child) {
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
),
body: Center(
child: SizedBox(
width: 600,
child: ListView(
children: [
TextFormField(
initialValue: settings.commandSeparator,
onChanged: (value) => settings.commandSeparator = value,
maxLength: 1,
decoration: const InputDecoration(
labelText: 'Command Separator',
helperText:
'The character that separates commands. To send it literally, use it twice.',
),
),
const SizedBox(height: 16),
CheckboxListTile.adaptive(
value: settings.echoCommands,
onChanged: (value) => setState(() => settings.echoCommands = value!),
title: const Text('Echo Commands'),
subtitle: const Text(
'Whether to echo commands to the screen as they are sent.'),
),
CheckboxListTile.adaptive(
value: settings.showTimestamps,
onChanged: (value) => setState(() => settings.showTimestamps = value!),
title: const Text('Show Timestamps'),
subtitle: const Text(
'Whether to show timestamps on messages received from the server.'),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
store.currentProfile.saveSettings(settings);
store.loadSettings();
Navigator.pop(context);
},
child: const Icon(Icons.save),
),
);
},
);
}
}