Migrate photobooth uploader to Avalonia

This commit is contained in:
Codex Agent
2026-01-12 17:20:35 +01:00
parent 6fe363640f
commit 75a9bcee12
15 changed files with 141 additions and 100 deletions

2
.gitignore vendored
View File

@@ -13,6 +13,8 @@ fotospiel-tenant-app
/storage/*.key /storage/*.key
/storage/pail /storage/pail
/vendor /vendor
/clients/photobooth-uploader/**/bin
/clients/photobooth-uploader/**/obj
.env .env
.env.backup .env.backup
.env.production .env.production

View File

@@ -1,18 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35013.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhotoboothUploader", "PhotoboothUploader\PhotoboothUploader.csproj", "{CDF88A75-8B20-4F54-96FC-A640B0D19A10}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CDF88A75-8B20-4F54-96FC-A640B0D19A10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CDF88A75-8B20-4F54-96FC-A640B0D19A10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CDF88A75-8B20-4F54-96FC-A640B0D19A10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CDF88A75-8B20-4F54-96FC-A640B0D19A10}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PhotoboothUploader.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

View File

@@ -0,0 +1,23 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace PhotoboothUploader;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}

View File

@@ -1,7 +0,0 @@
<Application
x:Class="PhotoboothUploader.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@@ -1,17 +0,0 @@
using Microsoft.UI.Xaml;
namespace PhotoboothUploader;
public partial class App : Application
{
public App()
{
InitializeComponent();
}
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
var window = new MainWindow();
window.Activate();
}
}

View File

@@ -0,0 +1,23 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="520" d:DesignHeight="360"
x:Class="PhotoboothUploader.MainWindow"
Width="520" Height="360"
Title="Fotospiel Photobooth Uploader">
<StackPanel Spacing="12" Margin="24" MaxWidth="420">
<TextBlock Text="Fotospiel Photobooth Uploader" FontSize="20" FontWeight="SemiBold" />
<TextBlock Text="Gib den 6-stelligen Verbindungscode ein." TextWrapping="Wrap" />
<TextBox x:Name="CodeBox" MaxLength="6" Watermark="123456" />
<Button x:Name="ConnectButton" Content="Verbinden" Click="ConnectButton_Click" />
<StackPanel Spacing="6">
<TextBlock Text="Upload-Ordner" FontWeight="SemiBold" />
<TextBlock x:Name="FolderText" Text="Noch nicht ausgewählt." TextWrapping="Wrap" />
<Button x:Name="PickFolderButton" Content="Ordner auswählen" Click="PickFolderButton_Click" IsEnabled="False" />
</StackPanel>
<TextBlock x:Name="StatusText" Text="Nicht verbunden." TextWrapping="Wrap" />
</StackPanel>
</Window>

View File

@@ -1,15 +1,16 @@
using Microsoft.UI.Xaml; using System;
using System.IO;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using PhotoboothUploader.Models; using PhotoboothUploader.Models;
using PhotoboothUploader.Services; using PhotoboothUploader.Services;
using System.Linq;
using System.IO;
using Windows.Storage;
using Windows.Storage.Pickers;
using WinRT.Interop;
namespace PhotoboothUploader; namespace PhotoboothUploader;
public sealed partial class MainWindow : Window public partial class MainWindow : Window
{ {
private const string DefaultBaseUrl = "https://fotospiel.app"; private const string DefaultBaseUrl = "https://fotospiel.app";
private PhotoboothConnectClient _client; private PhotoboothConnectClient _client;
@@ -28,7 +29,7 @@ public sealed partial class MainWindow : Window
ApplySettings(); ApplySettings();
} }
private async void ConnectButton_Click(object sender, RoutedEventArgs e) private async void ConnectButton_Click(object? sender, RoutedEventArgs e)
{ {
var code = (CodeBox.Text ?? string.Empty).Trim(); var code = (CodeBox.Text ?? string.Empty).Trim();
@@ -62,27 +63,27 @@ public sealed partial class MainWindow : Window
ConnectButton.IsEnabled = true; ConnectButton.IsEnabled = true;
} }
private async void PickFolderButton_Click(object sender, RoutedEventArgs e) private async void PickFolderButton_Click(object? sender, RoutedEventArgs e)
{ {
var picker = new FolderPicker var options = new FolderPickerOpenOptions
{ {
SuggestedStartLocation = PickerLocationId.PicturesLibrary, Title = "Upload-Ordner auswählen",
AllowMultiple = false,
}; };
picker.FileTypeFilter.Add("*"); var folders = await StorageProvider.OpenFolderPickerAsync(options);
InitializeWithWindow.Initialize(picker, WindowNative.GetWindowHandle(this)); var folder = folders.FirstOrDefault();
var localPath = folder?.TryGetLocalPath();
StorageFolder? folder = await picker.PickSingleFolderAsync(); if (string.IsNullOrWhiteSpace(localPath))
if (folder is null)
{ {
return; return;
} }
_settings.WatchFolder = folder.Path; _settings.WatchFolder = localPath;
_settingsStore.Save(_settings); _settingsStore.Save(_settings);
FolderText.Text = folder.Path; FolderText.Text = localPath;
StartUploadPipelineIfReady(); StartUploadPipelineIfReady();
} }
@@ -156,7 +157,7 @@ public sealed partial class MainWindow : Window
private void UpdateStatus(string message) private void UpdateStatus(string message)
{ {
DispatcherQueue.TryEnqueue(() => StatusText.Text = message); Dispatcher.UIThread.Post(() => StatusText.Text = message);
} }
private string? ResolveUploadUrl(string? uploadUrl) private string? ResolveUploadUrl(string? uploadUrl)

View File

@@ -1,28 +0,0 @@
<Window
x:Class="PhotoboothUploader.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Fotospiel Photobooth Uploader"
Height="360"
Width="520">
<Grid Padding="24">
<StackPanel Spacing="16" MaxWidth="420">
<TextBlock Text="Fotospiel Photobooth Uploader" FontSize="20" FontWeight="SemiBold" />
<TextBlock Text="Gib den 6-stelligen Verbindungscode ein." TextWrapping="Wrap" />
<TextBox x:Name="CodeBox" MaxLength="6" PlaceholderText="123456">
<TextBox.InputScope>
<InputScope>
<InputScopeName NameValue="Number" />
</InputScope>
</TextBox.InputScope>
</TextBox>
<Button x:Name="ConnectButton" Content="Verbinden" Click="ConnectButton_Click" />
<StackPanel Spacing="8">
<TextBlock Text="Upload-Ordner" FontWeight="SemiBold" />
<TextBlock x:Name="FolderText" Text="Noch nicht ausgewählt." TextWrapping="Wrap" />
<Button x:Name="PickFolderButton" Content="Ordner auswählen" Click="PickFolderButton_Click" IsEnabled="False" />
</StackPanel>
<TextBlock x:Name="StatusText" Text="Nicht verbunden." TextWrapping="Wrap" />
</StackPanel>
</Grid>
</Window>

View File

@@ -1,19 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<UseWinUI>true</UseWinUI>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<DisableImplicitWindowsAppSDKRuntimeIdentifiers>true</DisableImplicitWindowsAppSDKRuntimeIdentifiers>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<WindowsAppSDKRuntimeIdentifiers>win-x64</WindowsAppSDKRuntimeIdentifiers>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240404000" /> <PackageReference Include="Avalonia" Version="11.3.10" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.10" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.10" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.10" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.10">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,21 @@
using Avalonia;
using System;
namespace PhotoboothUploader;
class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}

View File

@@ -1,5 +1,9 @@
using System;
using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json; using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using PhotoboothUploader.Models; using PhotoboothUploader.Models;
namespace PhotoboothUploader.Services; namespace PhotoboothUploader.Services;

View File

@@ -1,3 +1,5 @@
using System;
using System.IO;
using System.Text.Json; using System.Text.Json;
using PhotoboothUploader.Models; using PhotoboothUploader.Models;

View File

@@ -1,6 +1,11 @@
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Channels; using System.Threading.Channels;
using System.Threading.Tasks;
using PhotoboothUploader.Models; using PhotoboothUploader.Models;
namespace PhotoboothUploader.Services; namespace PhotoboothUploader.Services;

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="PhotoboothUploader.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>