Compare commits
7 Commits
084c52ba2d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59c958531d | ||
|
|
3a65bec63f | ||
|
|
f2c33c549b | ||
|
|
7913809c54 | ||
|
|
463c1faec5 | ||
|
|
30c7d35c5d | ||
|
|
6a1c58e51d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/public/hot
|
||||
/public/storage
|
||||
/PhotoboothUploader/bin
|
||||
/PhotoboothUploader/build
|
||||
/PhotoboothUploader/obj
|
||||
/storage/*.key
|
||||
/storage/framework
|
||||
|
||||
29
Dockerfile
29
Dockerfile
@@ -1,18 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM node:20-alpine AS frontend_build
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --ignore-scripts
|
||||
|
||||
COPY resources resources
|
||||
COPY postcss.config.js tailwind.config.js vite.config.mjs jsconfig.json ./
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM php:8.3-fpm-alpine AS php_build
|
||||
FROM php:8.3-fpm-alpine AS php_deps
|
||||
|
||||
ENV COMPOSER_ALLOW_SUPERUSER=1
|
||||
|
||||
@@ -60,6 +48,21 @@ COPY . .
|
||||
RUN set -eux; \
|
||||
composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction --no-progress --no-scripts
|
||||
|
||||
FROM node:20-alpine AS frontend_build
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --ignore-scripts
|
||||
|
||||
COPY resources resources
|
||||
COPY postcss.config.js tailwind.config.js vite.config.mjs jsconfig.json ./
|
||||
COPY --from=php_deps /var/www/html/vendor/filament /var/www/html/vendor/filament
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM php_deps AS php_build
|
||||
|
||||
COPY --from=frontend_build /var/www/html/public/build /var/www/html/public/build
|
||||
|
||||
RUN set -eux; \
|
||||
|
||||
@@ -86,5 +86,11 @@
|
||||
<Setter Property="Background" Value="{DynamicResource SecondaryButtonBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SecondaryButtonTextBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="Button.cta">
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="18,12" />
|
||||
</Style>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Grid Margin="24,32,24,24" RowDefinitions="Auto,*">
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
|
||||
<Border Width="40" Height="40" Classes="card accent" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<Image Source="avares://PhotoboothUploader/Assets/logo.png" Width="28" Height="28" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<Image Source="avares://AIStylegallery.PhotoboothUploader/Assets/logo.png" Width="28" Height="28" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<StackPanel Spacing="2">
|
||||
<TextBlock x:Name="TitleText"
|
||||
@@ -20,123 +20,180 @@
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,16,*">
|
||||
<StackPanel Grid.Column="0" Spacing="16" MaxWidth="420">
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Text="Zugangsdaten" FontWeight="SemiBold" />
|
||||
<TextBlock Text="Upload-URL" />
|
||||
<TextBox x:Name="ManualUploadUrlBox" Watermark="https://stylegallery.fotospiel.app/api/v1/photobooth/upload" />
|
||||
<TextBlock Text="Benutzername" />
|
||||
<TextBox x:Name="ManualUsernameBox" />
|
||||
<TextBlock Text="Passwort" />
|
||||
<TextBox x:Name="ManualPasswordBox" PasswordChar="•" />
|
||||
<TextBlock Text="Antwort-Format (optional)" />
|
||||
<ComboBox x:Name="ResponseFormatBox" SelectedIndex="0">
|
||||
<ComboBoxItem Content="Auto" />
|
||||
<ComboBoxItem Content="JSON" />
|
||||
<ComboBoxItem Content="XML" />
|
||||
</ComboBox>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="TestConnectionButton" Content="Verbindung testen" Click="TestConnectionButton_Click" Classes="secondary" />
|
||||
<Button x:Name="SaveAdvancedButton" Content="Speichern" Click="SaveAdvancedButton_Click" Classes="primary" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Upload-Ordner" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="FolderText" Text="Noch nicht ausgewählt." TextWrapping="Wrap" Classes="subtitle" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="DslrBoothPresetButton" Content="DSLrBooth" Click="DslrBoothPresetButton_Click" Classes="secondary" IsVisible="False" />
|
||||
<Button x:Name="SparkboothPresetButton" Content="Photobooth (Sparkbooth)" Click="SparkboothPresetButton_Click" Classes="secondary" IsVisible="False" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="PickFolderButton" Content="Ordner auswählen" Click="PickFolderButton_Click" Classes="primary" IsEnabled="False" />
|
||||
<Button x:Name="TestUploadButton" Content="Test-Upload senden" Click="TestUploadButton_Click" Classes="secondary" IsEnabled="False" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Upload-Optionen" FontWeight="SemiBold" />
|
||||
<TextBlock Text="Max. parallele Uploads" />
|
||||
<TextBox x:Name="MaxUploadsBox" Watermark="2" />
|
||||
<TextBlock Text="Upload-Tempo" />
|
||||
<ComboBox x:Name="UploadTempoBox" SelectedIndex="1">
|
||||
<ComboBoxItem Content="Schnell (ohne Pause)" />
|
||||
<ComboBoxItem Content="Normal" />
|
||||
<ComboBoxItem Content="Sanft (schont Netzwerk)" />
|
||||
</ComboBox>
|
||||
<TextBlock Text="Nur diese Dateien (optional)" />
|
||||
<TextBox x:Name="IncludePatternsBox" Watermark="*.jpg;*.jpeg;*.png" />
|
||||
<TextBlock Text="Dateien ausschliessen (optional)" />
|
||||
<TextBox x:Name="ExcludePatternsBox" Watermark="*_preview*;*.tmp" />
|
||||
<TextBlock Text="Profile" />
|
||||
<ComboBox x:Name="ProfilesBox" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="LoadProfileButton" Content="Profil laden" Click="LoadProfileButton_Click" Classes="secondary" />
|
||||
<Button x:Name="SaveProfileButton" Content="Profil speichern" Click="SaveProfileButton_Click" Classes="secondary" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<ToggleSwitch x:Name="QuietToggle" Content="Ruhiger Modus (nur Fehler anzeigen)" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Spacing="16" MaxWidth="380" Margin="0,6,0,0">
|
||||
<Border Padding="14" Classes="card accent">
|
||||
<Grid Grid.Row="1" RowDefinitions="Auto,*">
|
||||
<Border Grid.Row="0" Padding="14" Classes="card accent" Margin="0,0,0,16">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="Status" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="StatusText" Text="Nicht verbunden." TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="LastUploadText" Text="Letzter Upload: —" />
|
||||
<TextBlock x:Name="QueueStatusText" Text="Warteschlange: 0 · Läuft: 0 · Fehlgeschlagen: 0" />
|
||||
<TextBlock x:Name="LiveStatusText" Text="Live: —" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="Details" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="EventNameText" Text="Benutzername: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="BaseUrlText" Text="Basis-URL: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="UploadUrlText" Text="Upload-URL: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="VersionText" Text="App-Version: —" />
|
||||
<TextBlock x:Name="ConnectExpiryText" Text="Antwort-Format: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="FolderHealthText" Text="Ordner: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="DiskFreeText" Text="Freier Speicher: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="LastSeenText" Text="Letzte Datei: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="LastErrorText" Text="Letzter Fehler: —" TextWrapping="Wrap" />
|
||||
<Button x:Name="LogCopyButton" Content="Log kopieren" Click="LogCopyButton_Click" Classes="secondary" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Letzte Uploads" FontWeight="SemiBold" />
|
||||
<ItemsControl x:Name="RecentUploadsList" ItemsSource="{Binding RecentUploads}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="#14FFFFFF" Padding="10" CornerRadius="8" Margin="0,0,0,8">
|
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto">
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding FileName}" />
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding StatusLabel}" />
|
||||
<TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding UpdatedLabel}" Opacity="0.7" FontSize="11" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="RetryFailedButton" Content="Fehlgeschlagene erneut senden" Click="RetryFailedButton_Click" IsEnabled="False" Classes="secondary" />
|
||||
<Button x:Name="ClearFailedButton" Content="Fehlerliste leeren" Click="ClearFailedButton_Click" IsEnabled="False" Classes="secondary" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock x:Name="LastUploadText" Text="Letzter Upload: —" />
|
||||
<TextBlock x:Name="QueueStatusText" Text="Warteschlange: 0 · Läuft: 0 · Fehlgeschlagen: 0" />
|
||||
<TextBlock x:Name="LiveStatusText" Text="Live: —" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Button x:Name="StartUploadsButton" Content="Upload starten" Click="StartUploadsButton_Click" Classes="primary cta" />
|
||||
<Button x:Name="PauseUploadsButton" Content="Upload pausieren" Click="PauseUploadsButton_Click" Classes="secondary cta" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<Grid x:Name="MainContentGrid" Grid.Row="1" ColumnDefinitions="*,16,*" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<StackPanel x:Name="LeftPanel" Grid.Column="0" Spacing="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<TabControl x:Name="MainTabs" VerticalAlignment="Stretch">
|
||||
<TabItem Header="Setup">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Spacing="16">
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Text="Zugangsdaten" FontWeight="SemiBold" />
|
||||
<TextBlock Text="Upload-URL" />
|
||||
<TextBox x:Name="ManualUploadUrlBox" Watermark="https://stylegallery.fotospiel.app/api/v1/photobooth/upload" />
|
||||
<TextBlock Text="Benutzername" />
|
||||
<TextBox x:Name="ManualUsernameBox" />
|
||||
<TextBlock Text="Passwort" />
|
||||
<TextBox x:Name="ManualPasswordBox" PasswordChar="•" />
|
||||
<TextBlock Text="Antwort-Format (optional)" />
|
||||
<ComboBox x:Name="ResponseFormatBox" SelectedIndex="0">
|
||||
<ComboBoxItem Content="Auto" />
|
||||
<ComboBoxItem Content="JSON" />
|
||||
<ComboBoxItem Content="XML" />
|
||||
</ComboBox>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="TestConnectionButton" Content="Verbindung testen" Click="TestConnectionButton_Click" Classes="secondary" />
|
||||
<Button x:Name="SaveAdvancedButton" Content="Speichern" Click="SaveAdvancedButton_Click" Classes="primary" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Upload">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Spacing="16">
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Upload-Ordner" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="FolderText" Text="Noch nicht ausgewählt." TextWrapping="Wrap" Classes="subtitle" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="DslrBoothPresetButton" Content="DSLrBooth" Click="DslrBoothPresetButton_Click" Classes="secondary" IsVisible="False" />
|
||||
<Button x:Name="SparkboothPresetButton" Content="Photobooth (Sparkbooth)" Click="SparkboothPresetButton_Click" Classes="secondary" IsVisible="False" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="PickFolderButton" Content="Ordner auswählen" Click="PickFolderButton_Click" Classes="primary" IsEnabled="False" />
|
||||
<Button x:Name="TestUploadButton" Content="Test-Upload senden" Click="TestUploadButton_Click" Classes="secondary" IsEnabled="False" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Erweitert">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Spacing="16">
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Upload-Optionen" FontWeight="SemiBold" />
|
||||
<TextBlock Text="Max. parallele Uploads" />
|
||||
<TextBox x:Name="MaxUploadsBox" Watermark="2" />
|
||||
<TextBlock Text="Upload-Tempo" />
|
||||
<ComboBox x:Name="UploadTempoBox" SelectedIndex="1">
|
||||
<ComboBoxItem Content="Schnell (ohne Pause)" />
|
||||
<ComboBoxItem Content="Normal" />
|
||||
<ComboBoxItem Content="Sanft (schont Netzwerk)" />
|
||||
</ComboBox>
|
||||
<TextBlock Text="Nur diese Dateien (optional)" />
|
||||
<TextBox x:Name="IncludePatternsBox" Watermark="*.jpg;*.jpeg;*.png" />
|
||||
<TextBlock Text="Dateien ausschliessen (optional)" />
|
||||
<TextBox x:Name="ExcludePatternsBox" Watermark="*_preview*;*.tmp" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Profile" FontWeight="SemiBold" />
|
||||
<ComboBox x:Name="ProfilesBox" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="LoadProfileButton" Content="Profil laden" Click="LoadProfileButton_Click" Classes="secondary" />
|
||||
<Button x:Name="SaveProfileButton" Content="Profil speichern" Click="SaveProfileButton_Click" Classes="secondary" />
|
||||
</StackPanel>
|
||||
<ToggleSwitch x:Name="QuietToggle" Content="Ruhiger Modus (nur Fehler anzeigen)" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Logs">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBox x:Name="LogTextBox"
|
||||
AcceptsReturn="True"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
MinHeight="180"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto" />
|
||||
<Button x:Name="LogCopyButton" Content="Log kopieren" Click="LogCopyButton_Click" Classes="secondary" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="RightPanel" Grid.Column="2" Spacing="16" Margin="0,6,0,0" HorizontalAlignment="Stretch">
|
||||
<Border x:Name="FirstRunCard" Padding="14" Classes="card">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="Erste Schritte" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="StepUploadUrlText" Text="1. Upload-URL speichern" />
|
||||
<TextBlock x:Name="StepFolderText" Text="2. Upload-Ordner wählen" />
|
||||
<TextBlock x:Name="StepReadyText" Text="3. Upload läuft" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Letzte Uploads" FontWeight="SemiBold" />
|
||||
<ItemsControl x:Name="RecentUploadsList" ItemsSource="{Binding RecentUploads}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="#14FFFFFF" Padding="10" CornerRadius="8" Margin="0,0,0,8">
|
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto">
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding FileName}" />
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding StatusLabel}" />
|
||||
<TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding UpdatedLabel}" Opacity="0.7" FontSize="11" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button x:Name="RetryFailedButton" Content="Fehlgeschlagene erneut senden" Click="RetryFailedButton_Click" IsEnabled="False" Classes="secondary" />
|
||||
<Button x:Name="ClearFailedButton" Content="Fehlerliste leeren" Click="ClearFailedButton_Click" IsEnabled="False" Classes="secondary" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="14" Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Diagnose" FontWeight="SemiBold" />
|
||||
<Expander Header="Details anzeigen">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock x:Name="EventNameText" Text="Benutzername: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="BaseUrlText" Text="Basis-URL: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="UploadUrlText" Text="Upload-URL: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="VersionText" Text="App-Version: —" />
|
||||
<TextBlock x:Name="ConnectExpiryText" Text="Antwort-Format: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="FolderHealthText" Text="Ordner: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="DiskFreeText" Text="Freier Speicher: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="LastSeenText" Text="Letzte Datei: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="LastErrorText" Text="Letzter Fehler: —" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -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<string> _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<string> 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()
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user