diff --git a/.gitignore b/.gitignore
index 1ae9faa..56c1d59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/public/hot
/public/storage
/PhotoboothUploader/bin
+/PhotoboothUploader/build
/PhotoboothUploader/obj
/storage/*.key
/storage/framework
diff --git a/PhotoboothUploader/App.axaml b/PhotoboothUploader/App.axaml
index 5b36685..eac59bf 100644
--- a/PhotoboothUploader/App.axaml
+++ b/PhotoboothUploader/App.axaml
@@ -86,5 +86,11 @@
+
+
diff --git a/PhotoboothUploader/MainWindow.axaml b/PhotoboothUploader/MainWindow.axaml
index e52aaeb..6b5f43e 100644
--- a/PhotoboothUploader/MainWindow.axaml
+++ b/PhotoboothUploader/MainWindow.axaml
@@ -10,7 +10,7 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PhotoboothUploader/MainWindow.axaml.cs b/PhotoboothUploader/MainWindow.axaml.cs
index 1aa01ab..b0282a7 100644
--- a/PhotoboothUploader/MainWindow.axaml.cs
+++ b/PhotoboothUploader/MainWindow.axaml.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
+using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
@@ -21,6 +22,7 @@ namespace PhotoboothUploader;
public partial class MainWindow : Window
{
private const string DefaultUploadUrl = "https://stylegallery.fotospiel.app/api/v1/photobooth/upload";
+ private const double ResponsiveBreakpoint = 900;
private readonly SettingsStore _settingsStore = new();
private readonly UploadService _uploadService = new();
private PhotoboothSettings _settings;
@@ -32,6 +34,7 @@ public partial class MainWindow : Window
private int _uploadingCount;
private int _failedCount;
private DateTimeOffset? _lastSuccessAt;
+ private bool _uploadsPaused;
private readonly DispatcherTimer _liveTimer = new();
private readonly List _logBuffer = new();
@@ -47,6 +50,7 @@ public partial class MainWindow : Window
_settings.UploadUrl = DefaultUploadUrl;
_settings.BaseUrl = ExtractBaseUrl(_settings.UploadUrl);
}
+ _uploadsPaused = _settings.UploadsPaused;
if (_settings.MaxConcurrentUploads <= 0)
{
_settings.MaxConcurrentUploads = 2;
@@ -60,6 +64,7 @@ public partial class MainWindow : Window
_liveTimer.Start();
Opened += OnWindowOpened;
Closing += OnWindowClosing;
+ SizeChanged += (_, _) => UpdateResponsiveLayout();
ApplySettings();
}
@@ -85,7 +90,9 @@ public partial class MainWindow : Window
FolderText.Text = localPath;
UpdateFolderHealth();
+ UpdateFirstRunChecklist();
StartUploadPipelineIfReady();
+ UpdateUploadControls();
}
private void DslrBoothPresetButton_Click(object? sender, RoutedEventArgs e)
@@ -116,7 +123,7 @@ public partial class MainWindow : Window
if (!string.IsNullOrWhiteSpace(_settings.UploadUrl))
{
- StatusText.Text = "Upload bereit.";
+ UpdateStatus("Upload bereit.");
PickFolderButton.IsEnabled = true;
TestUploadButton.IsEnabled = true;
StartUploadPipelineIfReady();
@@ -127,16 +134,27 @@ public partial class MainWindow : Window
TestUploadButton.IsEnabled = false;
}
+ if (_uploadsPaused)
+ {
+ _uploadService.Stop();
+ _watcher?.Dispose();
+ _watcher = null;
+ }
+
UpdateCountersText();
UpdateFolderHealth();
UpdateDiagnostics();
+ UpdateFirstRunChecklist();
+ UpdateLogPreview();
RefreshProfiles();
UpdatePresetButtons();
+ UpdateUploadControls();
}
private void OnWindowOpened(object? sender, EventArgs e)
{
ApplyWindowSize();
+ UpdateResponsiveLayout();
}
private void OnWindowClosing(object? sender, WindowClosingEventArgs e)
@@ -159,10 +177,57 @@ public partial class MainWindow : Window
}
}
+ private void UpdateResponsiveLayout()
+ {
+ if (MainContentGrid is null || LeftPanel is null || RightPanel is null)
+ {
+ return;
+ }
+
+ var isNarrow = Bounds.Width < ResponsiveBreakpoint;
+
+ if (isNarrow)
+ {
+ MainContentGrid.ColumnDefinitions = new ColumnDefinitions("1*");
+ MainContentGrid.RowDefinitions = new RowDefinitions("Auto,Auto");
+ MainContentGrid.RowSpacing = 16;
+ MainContentGrid.ColumnSpacing = 0;
+ RightPanel.Margin = new Thickness(0, 16, 0, 0);
+
+ Grid.SetColumn(LeftPanel, 0);
+ Grid.SetRow(LeftPanel, 0);
+
+ Grid.SetColumn(RightPanel, 0);
+ Grid.SetRow(RightPanel, 1);
+ }
+ else
+ {
+ MainContentGrid.ColumnDefinitions = new ColumnDefinitions("*,16,*");
+ MainContentGrid.RowDefinitions = new RowDefinitions("Auto");
+ MainContentGrid.RowSpacing = 0;
+ MainContentGrid.ColumnSpacing = 0;
+ RightPanel.Margin = new Thickness(0, 6, 0, 0);
+
+ Grid.SetColumn(LeftPanel, 0);
+ Grid.SetRow(LeftPanel, 0);
+
+ Grid.SetColumn(RightPanel, 2);
+ Grid.SetRow(RightPanel, 0);
+ }
+ }
+
private void StartUploadPipelineIfReady()
{
if (string.IsNullOrWhiteSpace(_settings.UploadUrl) || string.IsNullOrWhiteSpace(_settings.WatchFolder))
{
+ UpdateFirstRunChecklist();
+ UpdateUploadControls();
+ return;
+ }
+
+ if (_uploadsPaused)
+ {
+ UpdateUploadControls();
return;
}
@@ -170,6 +235,8 @@ public partial class MainWindow : Window
_uploadService.Start(_settings, OnQueued, OnUploading, OnSuccess, OnFailure);
StartWatcher(_settings.WatchFolder);
RestorePendingUploads();
+ UpdateFirstRunChecklist();
+ UpdateUploadControls();
}
private void StartWatcher(string folder)
@@ -228,7 +295,11 @@ public partial class MainWindow : Window
private void UpdateStatus(string message)
{
- Dispatcher.UIThread.Post(() => StatusText.Text = message);
+ Dispatcher.UIThread.Post(() =>
+ {
+ var pausedSuffix = _uploadsPaused ? " (PAUSIERT)" : string.Empty;
+ StatusText.Text = message + pausedSuffix;
+ });
}
private void OnQueued(string path)
@@ -494,11 +565,13 @@ public partial class MainWindow : Window
if (!string.IsNullOrWhiteSpace(_settings.UploadUrl))
{
- StatusText.Text = "Upload bereit.";
+ UpdateStatus("Upload bereit.");
PickFolderButton.IsEnabled = true;
TestUploadButton.IsEnabled = true;
}
UpdateStatus("Einstellungen gespeichert.");
+ UpdateFirstRunChecklist();
+ UpdateUploadControls();
}
private async void TestConnectionButton_Click(object? sender, RoutedEventArgs e)
@@ -558,7 +631,7 @@ public partial class MainWindow : Window
private async Task CreateSampleUploadAsync()
{
- var uri = new Uri("avares://PhotoboothUploader/Assets/sample-upload.png");
+ var uri = new Uri("avares://AIStylegallery.PhotoboothUploader/Assets/sample-upload.png");
await using var source = AssetLoader.Open(uri);
var tempPath = Path.Combine(Path.GetTempPath(), $"stylegallery-test-{DateTimeOffset.Now:yyyyMMddHHmmss}.png");
await using var target = File.Create(tempPath);
@@ -704,6 +777,80 @@ public partial class MainWindow : Window
});
}
+ private void UpdateUploadControls()
+ {
+ var ready = !string.IsNullOrWhiteSpace(_settings.UploadUrl)
+ && !string.IsNullOrWhiteSpace(_settings.WatchFolder);
+
+ StartUploadsButton.IsEnabled = ready && _uploadsPaused;
+ PauseUploadsButton.IsEnabled = ready && !_uploadsPaused;
+ }
+
+ private void StartUploadsButton_Click(object? sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(_settings.UploadUrl) || string.IsNullOrWhiteSpace(_settings.WatchFolder))
+ {
+ UpdateStatus("Bitte Upload-URL speichern und Ordner wählen.");
+ return;
+ }
+
+ if (!_uploadsPaused)
+ {
+ return;
+ }
+
+ _watcher?.Dispose();
+ _watcher = null;
+ _uploadsPaused = false;
+ _settings.UploadsPaused = false;
+ _settingsStore.Save(_settings);
+ UpdateStatus("Upload gestartet.");
+ StartUploadPipelineIfReady();
+ UpdateUploadControls();
+ }
+
+ private void PauseUploadsButton_Click(object? sender, RoutedEventArgs e)
+ {
+ if (_uploadsPaused)
+ {
+ return;
+ }
+
+ _uploadsPaused = true;
+ _settings.UploadsPaused = true;
+ _settingsStore.Save(_settings);
+ _uploadService.Stop();
+ _watcher?.Dispose();
+ _watcher = null;
+ UpdateStatus("Upload pausiert.");
+ UpdateUploadControls();
+ }
+
+ private void UpdateFirstRunChecklist()
+ {
+ var hasUpload = !string.IsNullOrWhiteSpace(_settings.UploadUrl);
+ var hasFolder = !string.IsNullOrWhiteSpace(_settings.WatchFolder);
+ var ready = hasUpload && hasFolder;
+
+ StepUploadUrlText.Text = hasUpload ? "1. Upload-URL speichern ✓" : "1. Upload-URL speichern";
+ StepFolderText.Text = hasFolder ? "2. Upload-Ordner wählen ✓" : "2. Upload-Ordner wählen";
+ StepReadyText.Text = ready ? "3. Upload läuft ✓" : "3. Upload läuft";
+ FirstRunCard.IsVisible = !ready;
+ }
+
+ private void UpdateLogPreview()
+ {
+ Dispatcher.UIThread.Post(() =>
+ {
+ if (LogTextBox is null)
+ {
+ return;
+ }
+
+ LogTextBox.Text = ReadLogForCopy();
+ });
+ }
+
private void SetLastError(string message)
{
_settings.LastError = message;
@@ -1050,6 +1197,8 @@ public partial class MainWindow : Window
RefreshProfiles();
ProfilesBox.SelectedIndex = 0;
UpdateStatus("Profil gespeichert.");
+ UpdateFirstRunChecklist();
+ UpdateUploadControls();
}
private async void LogCopyButton_Click(object? sender, RoutedEventArgs e)
@@ -1089,6 +1238,8 @@ public partial class MainWindow : Window
{
// ignore file errors
}
+
+ UpdateLogPreview();
}
private string ReadLogForCopy()
@@ -1137,7 +1288,9 @@ public partial class MainWindow : Window
_settingsStore.Save(_settings);
FolderText.Text = folder;
UpdateFolderHealth();
+ UpdateFirstRunChecklist();
StartUploadPipelineIfReady();
+ UpdateUploadControls();
}
private static string? GetDslrBoothFolder()
diff --git a/PhotoboothUploader/Models/PhotoboothSettings.cs b/PhotoboothUploader/Models/PhotoboothSettings.cs
index da83bc4..120afea 100644
--- a/PhotoboothUploader/Models/PhotoboothSettings.cs
+++ b/PhotoboothUploader/Models/PhotoboothSettings.cs
@@ -24,6 +24,7 @@ public sealed class PhotoboothSettings
public string? LastErrorAt { get; set; }
public int MaxConcurrentUploads { get; set; } = 2;
public int UploadDelayMs { get; set; } = 500;
+ public bool UploadsPaused { get; set; }
public double WindowWidth { get; set; }
public double WindowHeight { get; set; }
}
diff --git a/package-lock.json b/package-lock.json
index 67421ab..84e8443 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,7 +8,6 @@
"@fortawesome/fontawesome-svg-core": "^7.0.0",
"@fortawesome/free-solid-svg-icons": "^7.0.0",
"@fortawesome/vue-fontawesome": "^3.1.1",
- "@rollup/rollup-win32-x64-msvc": "^4.55.1",
"laravel-echo": "^2.1.7",
"pusher-js": "^8.4.0",
"vanilla-lazyload": "^19.1.3",
@@ -933,18 +932,6 @@
"win32"
]
},
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.55.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz",
- "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "os": [
- "win32"
- ]
- },
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
diff --git a/package.json b/package.json
index 3b11dcf..5b8c362 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,6 @@
"@fortawesome/fontawesome-svg-core": "^7.0.0",
"@fortawesome/free-solid-svg-icons": "^7.0.0",
"@fortawesome/vue-fontawesome": "^3.1.1",
- "@rollup/rollup-win32-x64-msvc": "^4.55.1",
"laravel-echo": "^2.1.7",
"pusher-js": "^8.4.0",
"vanilla-lazyload": "^19.1.3",