Skip to content

Commit

Permalink
Add Dark mode (#1954)
Browse files Browse the repository at this point in the history
* MF2025 - Make messaging between Frontend and Backend work (#1925)

* Delete files that should not be in git

* Init vite / tailwind

* Add Shadcn/ui

* Add playwright

* Added prettier incl tailwind plugin

* Move tailwind layers to index.css

* Added i18next

* Updated readme

* Added zustand and lodash

* Updated readme

* Include i18n correctly

* Enable correct HMR in devContainer

* Add vite options to npm run dev

* Add react-router

* Add WebView2 messaging to frontend

* Add .vite-folder to gitignore file

* Make communication between C# and javascript work

* Fully dynamic frontend messaging implemented

* Create message on Publisher Level

* Add unit tests

* JSON serialization/deserialization happens in MessageExchange

* Unit tests added and working

* MF2025 - Progress Bar (#1927)

* Add progress bar

* Reorganize panel in MainForm

* Move the playwright file to .github folder

* Create playwright test

* Update devContainer to support playwright

* Fix branch name

* MF2025 - Config List View - First version (#1934)

* Frontend shows config table = WIP

* First refactoring done for the ConfigItems

* WIP - ConfigItem View receiving Config

* 1st working version of the table with acutal config data

* Orphaned Serials Dialog is working correctly.

* Reverted accidental conflict of Name and DeviceName for config

* Fixed wrong clone

* Fixed facetted filtering

* Hide Status and Tags

* Filters are working correctly for the table

* Communication back and forth works for ConfigItem

* Improve the JSON serialization including unit tests

* Config Wizard opens correctly for OutputConfigItems

* Remove unused property "name"

* Serialize InputAction correctly

* Use dynamic way of serializeing ConfigItem and ModifierBase

* Clone InputConfigItem correctly

* fix the property name for ConfigEdit message

* Correctly open InputConfigWizard

* Fixed some visuals for the table

* Use short device type name

* Improved table rendering and active state can be toggled.

* Add output/input config items is possible

* Copy ConfigItem is possible

* All context menu actions are working.

* Fix config ref title, code cleanup

* Update workflow for PR build

* Add xml attrib deserialization info for backward compatibility

* Add status icons and inline name editing for config items

* Fix the table width

* Improved responsivness

* Working playwright tests for the config list

* Improving edit styles

* Fixing linter errors

* Status icons are working

* Fixed playwright test

* Fix failing build

* Fix xml unit tests to preserve temporary backward compatiblity

* Fixed Equals method for ConfigItem

* Fix the update after input config item is edited

* Display test mode status for config items

* Internationalization - Config list (#1940)

* Added i18n for config list

* Move cells and cells into own files

* Fix linter erros

* Fix playwright tests

* Update the github workflow to prevent double execution

* Translate missing waiting

* Fix linter issue

* Added WebView2 wrapper and threadsafe postWebMessageAsJson (#1941)

* Improved Config List Interactions (#1943)

* Quick access to edit, including double-click

* Remove select-none on individual components

* Sorting works for individual rows - first version

* Fix linter errors

* Introduce handle for dnd

* Add playwright test for DnD

* Small visual improvement for the handle

* i18n for types

* MobiFlight Project added (#1949)

* First stab at JSON config and project

* Update the expected XML files after removing "trigger" attribute

* Convert the pin to common Device using DeviceConfig

* Refactor LedModule to Device

* Refactored LCD Display

* Refactor Servo to as Device

* Refactored ShiftRegister to Device

* Refactor Stepper to be Device

* Refactor CustomDevice to Device

* Removed BCD device type

* JSON Serialization is working in a first version.
Requires more unit tests

* Improve JSON serialization

* Change frontend to be comaptible with new serialization format

* Output device panels are working

* device icons are displayed correctly in UI

* Config List View working

* Unit tests working

* Fixed unit tests.

* Fix an execution bug

* Fix linter

* Proper JSON Serialization and unit tests

* Add missing CustomDevice

* On "Add" send 1 item to the UI

* Use autofocus on edit

* Add mcc to default filter.

* Complete JSON serialization for modifiers, including Unit tests

* Fixed Output DeviceConfig serialization.

* use only short type names in JSON

* Add unit test for configreflist JSON serialization

* Improve Precondition serialization

* Don't include null values for MSFS2020 CustomInputAction

* No setting node handling

* Add more unit tests for open and save of files

* Fixed ShiftRegister and also added unit tests for it

* Added exception handling for ExecuteTestOn/Off

* Improved table responsiveness (#1953)

* Added theme and mode changer

* table now uses default colors for dark mode

* Fix linting error.

* Adjust colors for table header and grip

* add playwright UI test
  • Loading branch information
DocMoebiuz authored Mar 2, 2025
1 parent 33d7d4a commit 7e4f5ef
Show file tree
Hide file tree
Showing 268 changed files with 16,745 additions and 8,655 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: RunTests
on:
workflow_dispatch:
pull_request:
branches: [main]
branches: [main, mf2025/main]

env:
# Path to the solution file relative to the root of the project.
Expand Down Expand Up @@ -42,7 +42,11 @@ jobs:
run: "type Properties/AssemblyInfo.cs"
shell: pwsh

- name: Build
- name: Build frontend
working-directory: ${{env.GITHUB_WORKSPACE}}
run: cd frontend; npm install; npm run build

- name: Build core
working-directory: ${{env.GITHUB_WORKSPACE}}
# Add additional options to the MSBuild command line here (like platform or verbosity level).
# See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Playwright Tests
on:
pull_request:
branches: [ main, mf2025/main ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
- name: Run linter
run: npm run lint
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: frontend/playwright-report/
retention-days: 30
20 changes: 14 additions & 6 deletions Base/AppTelemetry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,35 @@ public void TrackStart()
GetClient().TrackEvent(trackingEvent);
}

public void ConfigLoaded(ConfigFile configFile)
public void ProjectLoaded(Project project)
{
EventTelemetry trackingEvent = new EventTelemetry("ProjectLoaded");
trackingEvent.Metrics["ConfigFiles"] = project.ConfigFiles.Count;
GetClient().TrackEvent(trackingEvent);
project.ConfigFiles.ForEach(configFile => ConfigLoaded((IConfigFile)configFile));
}

public void ConfigLoaded(IConfigFile configFile)
{
// Track config loaded event
EventTelemetry trackingEvent = new EventTelemetry("ConfigLoaded");
List<OutputConfigItem> outputConfigs = configFile.GetOutputConfigItems();
List<InputConfigItem> inputConfigs = configFile.GetInputConfigItems();
List<OutputConfigItem> outputConfigs = configFile.ConfigItems.Where(i => i is OutputConfigItem).Cast<OutputConfigItem>().ToList();
List<InputConfigItem> inputConfigs = configFile.ConfigItems.Where(i => i is InputConfigItem).Cast<InputConfigItem>().ToList();

foreach (OutputConfigItem item in outputConfigs)
{
String key = "output." + item.DisplayType;
String key = "output." + item.DeviceType;
if (!trackingEvent.Metrics.ContainsKey(key)) trackingEvent.Metrics[key] = 0;
trackingEvent.Metrics[key] += 1;

key = "output." + item.SourceType;
key = "output." + item.Source.SourceType;
if (!trackingEvent.Metrics.ContainsKey(key)) trackingEvent.Metrics[key] = 0;
trackingEvent.Metrics[key] += 1;
}

foreach (InputConfigItem item in inputConfigs)
{
String key = "input." + item.Type;
String key = "input." + item.DeviceType;
if (item.ModuleSerial.Contains(Joystick.SerialPrefix))
{
key += ".joystick";
Expand Down
187 changes: 39 additions & 148 deletions Base/ConfigFile.cs
Original file line number Diff line number Diff line change
@@ -1,177 +1,68 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Data;
using System.IO;
using MobiFlight.InputConfig;
using System.Linq;

namespace MobiFlight
namespace MobiFlight.Base
{
public class ConfigFile
public class ConfigFile : IConfigFile
{
public String FileName { get; set; }
System.Xml.XmlDocument xmlConfig = new System.Xml.XmlDocument();
public string FileName { get; set; }
public bool ReferenceOnly { get; set; } = false;
public bool EmbedContent { get; set; } = false;
public List<IConfigItem> ConfigItems { get; set; } = new List<IConfigItem>();

public ConfigFile() { }

public ConfigFile(String FileName)
public ConfigFile(string FileName)
{
this.FileName = FileName;
}

public void OpenFile()
{
if (FileName == null) throw new Exception("File yet not set");
xmlConfig.Load(FileName);
}

public void SaveFile(DataSet outputConfig, DataSet inputConfig)
{
xmlConfig.RemoveAll();
XmlDeclaration xmlDeclaration = xmlConfig.CreateXmlDeclaration("1.0", "UTF-8", "yes");
xmlConfig.InsertBefore(xmlDeclaration, xmlConfig.DocumentElement);

XmlElement root = xmlConfig.CreateElement("MobiflightConnector");
// Create a new element and add it to the document.
StringWriter sw = new StringWriter();
outputConfig.WriteXml(sw, XmlWriteMode.IgnoreSchema);
string s = sw.ToString();
XmlDocument tmpDoc = new XmlDocument();
tmpDoc.LoadXml(s);

XmlElement outputs = xmlConfig.CreateElement("outputs");
outputs.InnerXml = tmpDoc.DocumentElement.SelectSingleNode("/outputs").InnerXml;

sw = new StringWriter();
inputConfig.WriteXml(sw, XmlWriteMode.IgnoreSchema);
s = sw.ToString();
tmpDoc = new XmlDocument();
tmpDoc.LoadXml(s);

XmlElement inputs = xmlConfig.CreateElement("inputs");
inputs.InnerXml = tmpDoc.DocumentElement.SelectSingleNode("/inputs").InnerXml;

root.AppendChild(outputs);
root.AppendChild(inputs);
xmlConfig.AppendChild(root);
xmlConfig.Save(FileName);
}

private XmlReader getConfig(String xpath)
{
// first try the new way... if this fails try the old way
System.Xml.XmlNode outputConfig = xmlConfig.DocumentElement.SelectSingleNode(xpath);
if (outputConfig == null) throw new InvalidExpressionException();

System.IO.StringReader reader = new System.IO.StringReader(outputConfig.OuterXml);
System.Xml.XmlReader xReader = System.Xml.XmlReader.Create(reader);
return xReader;
}

public List<OutputConfigItem> GetOutputConfigItems()
{
List<OutputConfigItem> result = new List<OutputConfigItem>();

XmlNodeList outputs = xmlConfig.DocumentElement.SelectNodes("outputs/config/settings");
foreach(XmlNode item in outputs)
if (EmbedContent)
{
OutputConfigItem config = new OutputConfigItem();
System.IO.StringReader reader = new System.IO.StringReader(item.OuterXml);
System.Xml.XmlReader xReader = System.Xml.XmlReader.Create(reader);
config.ReadXml(xReader);
result.Add(config);
// Content is embedded, no need to load from file
return;
}

return result;
var json = File.ReadAllText(FileName);
var configFile = JsonConvert.DeserializeObject<ConfigFile>(json);
FileName = configFile.FileName;
ReferenceOnly = configFile.ReferenceOnly;
EmbedContent = configFile.EmbedContent;
ConfigItems = configFile.ConfigItems;
}

internal List<InputConfigItem> GetInputConfigItems()
public void SaveFile()
{
List<InputConfigItem> result = new List<InputConfigItem>();

XmlNodeList inputs = xmlConfig.DocumentElement.SelectNodes("inputs/config/settings");
foreach (XmlNode item in inputs)
if (EmbedContent || ReferenceOnly)
{
InputConfigItem config = new InputConfigItem();
System.IO.StringReader reader = new System.IO.StringReader(item.OuterXml);
System.Xml.XmlReader xReader = System.Xml.XmlReader.Create(reader);
xReader.Read();
config.ReadXml(xReader);
result.Add(config);
// Content is embedded or read-only, no need to save to file
return;
}

return result;
var json = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(FileName, json);
}

public XmlReader getInputConfig()
public string ToJson()
{
XmlReader result = null;

try
{
if (xmlConfig.DocumentElement == null) OpenFile();

result = getConfig("/MobiflightConnector/inputs");
}
catch (InvalidExpressionException e)
{
throw e;
}
catch (Exception e)
{
throw new ConfigErrorException("Error reading config", e);
}
return result;
return JsonConvert.SerializeObject(this, Formatting.Indented);
}

public XmlReader getOutputConfig()
public override bool Equals(object obj)
{
XmlReader result = null;

bool fallback = false;
try
{
if (xmlConfig.DocumentElement == null) OpenFile();
result = getConfig("/MobiflightConnector/outputs");
}
catch (InvalidExpressionException e)
{
fallback = true;
}
catch (Exception e)
{
throw new ConfigErrorException("Error reading config", e);
}

if (fallback)
{
fallback = false;
// fallback for old configs
try
{
result = getConfig("/MobiflightConnector");
}
catch (Exception ex)
{
fallback = true;
}
}

if (fallback)
{
fallback = false;
// fallback for old configs
try
{
result = getConfig("/ArcazeUsbConnector");
}
catch (Exception ex)
{
throw new Exception("Error: Loading config");
}
}

return result;
if (obj == null || !(obj is ConfigFile)) return false;
var other = obj as ConfigFile;

return
FileName.AreEqual(other.FileName) &&
ReferenceOnly == other.ReferenceOnly &&
EmbedContent == other.EmbedContent &&
ConfigItems.SequenceEqual(other.ConfigItems)
;
}
}
}
}
36 changes: 36 additions & 0 deletions Base/ConfigFileFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.IO;

namespace MobiFlight.Base
{
public static class ConfigFileFactory
{
public static IConfigFile CreateConfigFile(string filePath)
{
if (IsJson(filePath))
{
return new ConfigFile { FileName = filePath };
}
else if (IsXml(filePath))
{
return new DeprecatedConfigFile { FileName = filePath };
}
else
{
throw new InvalidDataException("Unsupported file format.");
}
}

private static bool IsJson(string filePath)
{
var firstChar = File.ReadAllText(filePath).TrimStart()[0];
return firstChar == '{' || firstChar == '[';
}

private static bool IsXml(string filePath)
{
var firstFewChars = File.ReadAllText(filePath).TrimStart().Substring(0, 5);
return firstFewChars.StartsWith("<?xml");
}
}
}

44 changes: 44 additions & 0 deletions Base/ConfigFileWrapperXML.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Xml.Serialization;

namespace MobiFlight.Base
{
[XmlRoot("config")]
public class ConfigFileXmlItem
{

[XmlAttribute("guid")]
public string guid { get; set; }

[XmlElement]
public bool active { get; set; }

[XmlElement]
public string description { get; set; }
}

public class OutputConfigFileXmlElement : ConfigFileXmlItem
{
[XmlElement]
public OutputConfigItem settings { get; set; }
}

public class InputConfigFileXmlElement : ConfigFileXmlItem
{
[XmlElement]
public InputConfigItem settings { get; set; }
}


[XmlRoot("MobiflightConnector")]
public class ConfigFileWrapperXML
{
[XmlArray("outputs")]
[XmlArrayItem("config")]
public List<OutputConfigFileXmlElement> outputConfigs;

[XmlArray("inputs")]
[XmlArrayItem("config")]
public List<InputConfigFileXmlElement> inputConfigs;
}
}
Loading

0 comments on commit 7e4f5ef

Please sign in to comment.