using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Collections.ObjectModel; using Wpf.Ui.Mvvm.Contracts; using Bloxstrap.UI.Elements.Dialogs; using Newtonsoft.Json.Linq; using System.Xml.Linq; namespace Bloxstrap.UI.Elements.Menu.Pages { /// /// Interaction logic for FastFlagEditorPage.xaml /// public partial class FastFlagEditorPage { // believe me when i say there is absolutely zero point to using mvvm for this // using a datagrid is a codebehind thing only and thats it theres literally no way around it private readonly ObservableCollection _fastFlagList = new(); private readonly List _validPrefixes = new() { "FFlag", "DFFlag", "SFFlag", "FInt", "DFInt", "FString", "DFString", "FLog", "DFlog" }; private bool _showPresets = false; private string _searchFilter = ""; public FastFlagEditorPage() { InitializeComponent(); } private void ReloadList() { var selectedEntry = DataGrid.SelectedItem as FastFlag; _fastFlagList.Clear(); var presetFlags = FastFlagManager.PresetFlags.Values; foreach (var pair in App.FastFlags.Prop.OrderBy(x => x.Key)) { if (!_showPresets && presetFlags.Contains(pair.Key)) continue; if (!pair.Key.ToLower().Contains(_searchFilter.ToLower())) continue; var entry = new FastFlag { // Enabled = true, Name = pair.Key, Value = pair.Value.ToString()! }; /* if (entry.Name.StartsWith("Disable")) { entry.Enabled = false; entry.Name = entry.Name[7..]; } */ _fastFlagList.Add(entry); } if (DataGrid.ItemsSource is null) DataGrid.ItemsSource = _fastFlagList; if (selectedEntry is null) return; var newSelectedEntry = _fastFlagList.Where(x => x.Name == selectedEntry.Name).FirstOrDefault(); if (newSelectedEntry is null) return; DataGrid.SelectedItem = newSelectedEntry; DataGrid.ScrollIntoView(newSelectedEntry); } private void ClearSearch(bool refresh = true) { SearchTextBox.Text = ""; _searchFilter = ""; if (refresh) ReloadList(); } private void ShowAddDialog() { var dialog = new AddFastFlagDialog(); dialog.ShowDialog(); if (dialog.Result != MessageBoxResult.OK) return; if (dialog.Tabs.SelectedIndex == 0) AddSingle(dialog.FlagNameTextBox.Text, dialog.FlagValueTextBox.Text); else if (dialog.Tabs.SelectedIndex == 1) ImportJSON(dialog.JsonTextBox.Text); } private void AddSingle(string name, string value) { FastFlag? entry; if (App.FastFlags.GetValue(name) is null) { if (!ValidateFlagEntry(name, value)) { ShowAddDialog(); return; } entry = new FastFlag { // Enabled = true, Name = name, Value = value }; if (!name.Contains(_searchFilter)) ClearSearch(); _fastFlagList.Add(entry); App.FastFlags.SetValue(entry.Name, entry.Value); } else { Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information); bool refresh = false; if (!_showPresets && FastFlagManager.PresetFlags.Values.Contains(name)) { TogglePresetsButton.IsChecked = true; _showPresets = true; refresh = true; } if (!name.Contains(_searchFilter)) { ClearSearch(false); refresh = true; } if (refresh) ReloadList(); entry = _fastFlagList.Where(x => x.Name == name).FirstOrDefault(); } DataGrid.SelectedItem = entry; DataGrid.ScrollIntoView(entry); } private void ImportJSON(string json) { Dictionary? list = null; json = json.Trim(); // autocorrect where possible if (!json.StartsWith('{')) json = '{' + json; if (!json.EndsWith('}')) json += '}'; try { var options = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }; list = JsonSerializer.Deserialize>(json, options); if (list is null) throw new Exception("JSON deserialization returned null"); } catch (Exception ex) { Frontend.ShowMessageBox( String.Format(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidJSON, ex.Message), MessageBoxImage.Error ); ShowAddDialog(); return; } if (list.Count > 16) { var result = Frontend.ShowMessageBox( Bloxstrap.Resources.Strings.Menu_FastFlagEditor_LargeConfig, MessageBoxImage.Warning, MessageBoxButton.YesNo ); if (result != MessageBoxResult.Yes) return; } var conflictingFlags = App.FastFlags.Prop.Where(x => list.ContainsKey(x.Key)).Select(x => x.Key); bool overwriteConflicting = false; if (conflictingFlags.Any()) { int count = conflictingFlags.Count(); string message = String.Format( Bloxstrap.Resources.Strings.Menu_FastFlagEditor_ConflictingImport, count, String.Join(", ", conflictingFlags.Take(25)) ); if (count > 25) message += "..."; var result = Frontend.ShowMessageBox(message, MessageBoxImage.Question, MessageBoxButton.YesNo); overwriteConflicting = result == MessageBoxResult.Yes; } foreach (var pair in list) { if (App.FastFlags.Prop.ContainsKey(pair.Key) && !overwriteConflicting) continue; var val = pair.Value.ToString(); if (val is null) continue; if (!ValidateFlagEntry(pair.Key, val)) continue; App.FastFlags.SetValue(pair.Key, pair.Value); } ClearSearch(); } private bool ValidateFlagEntry(string name, string value) { string lowerValue = value.ToLowerInvariant(); string errorMessage = ""; if (!_validPrefixes.Where(x => name.StartsWith(x)).Any()) errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidPrefix; else if (!name.All(x => char.IsLetterOrDigit(x) || x == '_')) errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidCharacter; else if ((name.StartsWith("FInt") || name.StartsWith("DFInt")) && !Int32.TryParse(value, out _)) errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidNumberValue; else if ((name.StartsWith("FFlag") || name.StartsWith("DFFlag")) && lowerValue != "true" && lowerValue != "false") errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidBoolValue; if (!String.IsNullOrEmpty(errorMessage)) { Frontend.ShowMessageBox(String.Format(errorMessage, name), MessageBoxImage.Error); return false; } return true; } // refresh list on page load to synchronize with preset page private void Page_Loaded(object sender, RoutedEventArgs e) => ReloadList(); private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) { int index = e.Row.GetIndex(); FastFlag entry = _fastFlagList[index]; var textbox = e.EditingElement as TextBox; if (textbox is null) return; switch (e.Column.Header) { case "Name": string oldName = entry.Name; string newName = textbox.Text; if (newName == oldName) return; if (App.FastFlags.GetValue(newName) is not null) { Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information); e.Cancel = true; textbox.Text = oldName; return; } App.FastFlags.SetValue(oldName, null); App.FastFlags.SetValue(newName, entry.Value); if (!newName.Contains(_searchFilter)) ClearSearch(); entry.Name = newName; break; case "Value": string oldValue = entry.Value; string newValue = textbox.Text; if (!ValidateFlagEntry(entry.Name, newValue)) { e.Cancel = true; textbox.Text = oldValue; return; } App.FastFlags.SetValue(entry.Name, newValue); break; } } private void BackButton_Click(object sender, RoutedEventArgs e) { if (Window.GetWindow(this) is INavigationWindow window) window.Navigate(typeof(FastFlagsPage)); } private void AddButton_Click(object sender, RoutedEventArgs e) => ShowAddDialog(); private void DeleteButton_Click(object sender, RoutedEventArgs e) { var tempList = new List(); foreach (FastFlag entry in DataGrid.SelectedItems) tempList.Add(entry); foreach (FastFlag entry in tempList) { _fastFlagList.Remove(entry); App.FastFlags.SetValue(entry.Name, null); } } private void ToggleButton_Click(object sender, RoutedEventArgs e) { if (sender is not ToggleButton button) return; _showPresets = button.IsChecked ?? false; ReloadList(); } private void ExportJSONButton_Click(object sender, RoutedEventArgs e) { string json = JsonSerializer.Serialize(App.FastFlags.Prop, new JsonSerializerOptions { WriteIndented = true }); Clipboard.SetDataObject(json); Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_JsonCopiedToClipboard, MessageBoxImage.Information); } private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e) { if (sender is not TextBox textbox) return; _searchFilter = textbox.Text; ReloadList(); } } }