mirror of
https://github.com/chenasraf/mudblock.git
synced 2026-05-18 01:48:57 +00:00
feat: settings page
This commit is contained in:
@@ -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}');
|
||||
|
||||
41
lib/core/features/settings.dart
Normal file
41
lib/core/features/settings.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
class Settings {
|
||||
String commandSeparator = ';';
|
||||
bool echoCommands = true;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
78
lib/pages/settings_page.dart
Normal file
78
lib/pages/settings_page.dart
Normal 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),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user