Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions dotnet-winforms-net48/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

# Copy this file from ".env.sample" to ".env", then fill in these values
# A Ditto AppID and Playground token can be obtained from https://portal.ditto.live
DITTO_APP_ID=""
DITTO_PLAYGROUND_TOKEN=""
DITTO_AUTH_URL=""
DITTO_WEBSOCKET_URL=""
6 changes: 6 additions & 0 deletions dotnet-winforms-net48/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>
73 changes: 73 additions & 0 deletions dotnet-winforms-net48/AppConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.IO;
using System.Text.RegularExpressions;

namespace Taskapp.WinForms.Net48
{
/// <summary>
/// Loads and provides access to application configuration from .env file
/// </summary>
public static class AppConfiguration
{
public static string AppId { get; private set; }
public static string PlaygroundToken { get; private set; }
public static string AuthUrl { get; private set; }
public static string WebsocketUrl { get; private set; }

/// <summary>
/// Loads configuration from .env file in the application directory
/// </summary>
public static void Load()
{
var envPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".env");

if (!File.Exists(envPath))
{
throw new FileNotFoundException("Configuration file .env not found", envPath);
}

var lines = File.ReadAllLines(envPath);

foreach (var line in lines)
{
// Skip empty lines and comments
if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith("#"))
continue;

// Parse KEY=VALUE or KEY="VALUE" format
var match = Regex.Match(line, @"^\s*([A-Z_]+)\s*=\s*""?([^""]+)""?\s*$");
if (match.Success)
{
var key = match.Groups[1].Value;
var value = match.Groups[2].Value;

switch (key)
{
case "DITTO_APP_ID":
AppId = value;
break;
case "DITTO_PLAYGROUND_TOKEN":
PlaygroundToken = value;
break;
case "DITTO_AUTH_URL":
AuthUrl = value;
break;
case "DITTO_WEBSOCKET_URL":
WebsocketUrl = value;
break;
}
}
}

// Validate required fields
if (string.IsNullOrWhiteSpace(AppId))
throw new InvalidOperationException("DITTO_APP_ID is required in .env file");
if (string.IsNullOrWhiteSpace(PlaygroundToken))
throw new InvalidOperationException("DITTO_PLAYGROUND_TOKEN is required in .env file");
if (string.IsNullOrWhiteSpace(AuthUrl))
throw new InvalidOperationException("DITTO_AUTH_URL is required in .env file");
if (string.IsNullOrWhiteSpace(WebsocketUrl))
throw new InvalidOperationException("DITTO_WEBSOCKET_URL is required in .env file");
}
}
}
54 changes: 54 additions & 0 deletions dotnet-winforms-net48/InputDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Windows.Forms;

namespace Taskapp.WinForms.Net48
{
/// <summary>
/// Simple input dialog for getting text input from user
/// </summary>
public static class InputDialog
{
public static string Show(string prompt, string title, string defaultValue = "")
{
using (Form form = new Form())
{
Label label = new Label();
TextBox textBox = new TextBox();
Button buttonOk = new Button();
Button buttonCancel = new Button();

form.Text = title;
label.Text = prompt;
textBox.Text = defaultValue;

buttonOk.Text = "OK";
buttonCancel.Text = "Cancel";
buttonOk.DialogResult = DialogResult.OK;
buttonCancel.DialogResult = DialogResult.Cancel;

label.SetBounds(9, 20, 372, 13);
textBox.SetBounds(12, 36, 372, 20);
buttonOk.SetBounds(228, 72, 75, 23);
buttonCancel.SetBounds(309, 72, 75, 23);

label.AutoSize = true;
textBox.Anchor = textBox.Anchor | AnchorStyles.Right;
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;

form.ClientSize = new System.Drawing.Size(396, 107);
form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel });
form.ClientSize = new System.Drawing.Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.StartPosition = FormStartPosition.CenterScreen;
form.MinimizeBox = false;
form.MaximizeBox = false;
form.AcceptButton = buttonOk;
form.CancelButton = buttonCancel;

DialogResult dialogResult = form.ShowDialog();
return dialogResult == DialogResult.OK ? textBox.Text : null;
}
}
}
}
65 changes: 65 additions & 0 deletions dotnet-winforms-net48/LoadingForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions dotnet-winforms-net48/LoadingForm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Taskapp.WinForms.Net48
{
/// <summary>
/// Loading form shown during async initialization
/// </summary>
public partial class LoadingForm : Form
{
private readonly Task _initializationTask;

public LoadingForm(Task initializationTask)
{
_initializationTask = initializationTask;
InitializeComponent();
}

private async void LoadingForm_Load(object sender, EventArgs e)
{
try
{
await _initializationTask;
DialogResult = DialogResult.OK;
Close();
}
catch (Exception ex)
{
MessageBox.Show(
$"Initialization failed: {ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
DialogResult = DialogResult.Cancel;
Close();
}
}
}
}
Loading