Skip to content
Merged
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
36 changes: 36 additions & 0 deletions src/Models/SimulateConfigModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using CommunityToolkit.Mvvm.ComponentModel;

namespace GeneralUpdate.Tools.Models;

/// <summary>
/// Configuration for the simulate-update module.
/// </summary>
public partial class SimulateConfigModel : ObservableObject
{
/// <summary>User-provided old-version app directory to test against.</summary>
[ObservableProperty] private string _appDirectory = string.Empty;

/// <summary>Path to a patch .zip generated by the Patch module.</summary>
[ObservableProperty] private string _patchFilePath = string.Empty;

/// <summary>The current version of the app being tested.</summary>
[ObservableProperty] private string _currentVersion = "1.0.0.0";

/// <summary>The target version the patch upgrades to.</summary>
[ObservableProperty] private string _targetVersion = "2.0.0.0";

/// <summary>Platform selector (1=Windows, 2=Linux).</summary>
[ObservableProperty] private int _platform = 1;

/// <summary>AppType sent to the server (1=ClientApp, 2=UpgradeApp).</summary>
[ObservableProperty] private int _appType = 1;

/// <summary>Application secret key for the update API.</summary>
[ObservableProperty] private string _appSecretKey = "dfeb5833-975e-4afb-88f1-6278ee9aeff6";

/// <summary>Product identifier.</summary>
[ObservableProperty] private string _productId = "2d974e2a-31e6-4887-9bb1-b4689e98c77a";

/// <summary>Directory where client.cs / upgrade.cs and server are generated.</summary>
[ObservableProperty] private string _outputDirectory = string.Empty;
}
42 changes: 42 additions & 0 deletions src/Services/LocalizationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,27 @@ public string this[string key]
["Theme.Light"] = "浅色",
["Theme.Dark"] = "深色",
["Theme.Toggle"] = "切换主题",
["Nav.Simulate"] = "模拟更新",
["Sim.Title"] = "模拟更新",
["Sim.TestTarget"] = "测试目标",
["Sim.OldAppDir"] = "旧版本应用目录",
["Sim.PatchFile"] = "补丁包文件",
["Sim.Select"] = "选择",
["Sim.UpdateConfig"] = "更新配置",
["Sim.CurrentVer"] = "当前版本",
["Sim.TargetVer"] = "目标版本",
["Sim.Platform"] = "平台",
["Sim.AppType"] = "应用类型",
["Sim.AppSecret"] = "应用密钥",
["Sim.ProductId"] = "产品ID",
["Sim.Output"] = "输出",
["Sim.OutputDir"] = "模拟目录",
["Sim.Start"] = "开始模拟",
["Sim.SelectAppDir"] = "选择旧版本应用目录",
["Sim.SelectPatch"] = "选择补丁包",
["Sim.SelectOutput"] = "选择模拟输出目录",
["Sim.ValidateDirs"] = "请填写所有必填项",
["Sim.DotnetCheck"] = "需要 .NET 10.0 SDK,请先安装",
},
["en-US"] = new()
{
Expand Down Expand Up @@ -187,6 +208,27 @@ public string this[string key]
["Theme.Light"] = "Light",
["Theme.Dark"] = "Dark",
["Theme.Toggle"] = "Toggle Theme",
["Nav.Simulate"] = "Simulate",
["Sim.Title"] = "Simulate Update",
["Sim.TestTarget"] = "Test Target",
["Sim.OldAppDir"] = "Old App Directory",
["Sim.PatchFile"] = "Patch Package",
["Sim.Select"] = "Select",
["Sim.UpdateConfig"] = "Update Config",
["Sim.CurrentVer"] = "Current Version",
["Sim.TargetVer"] = "Target Version",
["Sim.Platform"] = "Platform",
["Sim.AppType"] = "App Type",
["Sim.AppSecret"] = "App Secret",
["Sim.ProductId"] = "Product ID",
["Sim.Output"] = "Output",
["Sim.OutputDir"] = "Simulate Directory",
["Sim.Start"] = "Start Simulation",
["Sim.SelectAppDir"] = "Select old version directory",
["Sim.SelectPatch"] = "Select patch package",
["Sim.SelectOutput"] = "Select simulation output directory",
["Sim.ValidateDirs"] = "Please fill in all required fields",
["Sim.DotnetCheck"] = ".NET 10.0 SDK is required. Please install it first.",
}
};

Expand Down
4 changes: 3 additions & 1 deletion src/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ private void SyncNavItems()
NavItems.Add(new("Patch", _loc["Nav.Patch"], typeof(PatchViewModel), true));
NavItems.Add(new("Extension", _loc["Nav.Extension"], typeof(ExtensionViewModel), false));
NavItems.Add(new("OSS", _loc["Nav.OSS"], typeof(OSSViewModel), false));
NavItems.Add(new("Simulate", _loc["Nav.Simulate"], typeof(SimulateViewModel), false));
}

[RelayCommand]
Expand All @@ -46,7 +47,8 @@ private void Navigate(NavItem item)
{
"Patch" => new PatchViewModel(),
"Extension" => new ExtensionViewModel(),
_ => new OSSViewModel()
"OSS" => new OSSViewModel(),
_ => new SimulateViewModel()
};
}

Expand Down
76 changes: 76 additions & 0 deletions src/ViewModels/SimulateViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using GeneralUpdate.Tools.Models;
using GeneralUpdate.Tools.Services;

namespace GeneralUpdate.Tools.ViewModels;

public partial class SimulateViewModel : ViewModelBase
{
private readonly LocalizationService _loc = LocalizationService.Instance;

public SimulateConfigModel Config { get; } = new();

[ObservableProperty] private bool _isRunning;
[ObservableProperty] private string _status;
[ObservableProperty] private ObservableCollection<string> _log = new();

public ObservableCollection<PlatformItem> Platforms { get; } = new()
{
new(1, "Windows"),
new(2, "Linux")
};

public ObservableCollection<AppTypeItem> AppTypes { get; } = new()
{
new(1, "ClientApp"),
new(2, "UpgradeApp")
};

public SimulateViewModel()
{
_status = _loc["Patch.Ready"];
}

async Task<string?> PickFolder(string title)
{
var tl = Avalonia.Controls.TopLevel.GetTopLevel(
(Avalonia.Application.Current?.ApplicationLifetime as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime)?.MainWindow);
if (tl == null) return null;
var r = await tl.StorageProvider.OpenFolderPickerAsync(
new Avalonia.Platform.Storage.FolderPickerOpenOptions { Title = title, AllowMultiple = false });
return r.Count > 0 ? r[0].Path.LocalPath : null;
}

async Task<string?> PickFile(string title)
{
var tl = Avalonia.Controls.TopLevel.GetTopLevel(
(Avalonia.Application.Current?.ApplicationLifetime as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime)?.MainWindow);
if (tl == null) return null;
var r = await tl.StorageProvider.OpenFilePickerAsync(
new Avalonia.Platform.Storage.FilePickerOpenOptions { Title = title, AllowMultiple = false });
return r.Count > 0 ? r[0].Path.LocalPath : null;
}

[RelayCommand] async Task SelectAppDir() { var p = await PickFolder("选择旧版本应用目录"); if (p != null) Config.AppDirectory = p; }
[RelayCommand] async Task SelectPatch() { var p = await PickFile("选择补丁包"); if (p != null) Config.PatchFilePath = p; }
[RelayCommand] async Task SelectOutputDir() { var p = await PickFolder("选择模拟输出目录"); if (p != null) Config.OutputDirectory = p; }

[RelayCommand]
async Task StartSimulation()
{
// Validation will be implemented in issue #4
if (string.IsNullOrWhiteSpace(Config.AppDirectory)) { Status = "请选择旧版本应用目录"; return; }
if (string.IsNullOrWhiteSpace(Config.PatchFilePath)) { Status = "请选择补丁包文件"; return; }
if (string.IsNullOrWhiteSpace(Config.OutputDirectory)) { Status = "请选择模拟输出目录"; return; }
Status = "模拟功能将在后续 issue 中实现";
}

void L(string msg) => Log.Add($"[{DateTime.Now:HH:mm:ss}] {msg}");
}

public record PlatformItem(int Value, string DisplayName) { public override string ToString() => DisplayName; }
public record AppTypeItem(int Value, string DisplayName) { public override string ToString() => DisplayName; }
84 changes: 84 additions & 0 deletions src/Views/SimulateView.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:GeneralUpdate.Tools.ViewModels"
xmlns:svc="using:GeneralUpdate.Tools.Services"
x:Class="GeneralUpdate.Tools.Views.SimulateView"
x:DataType="vm:SimulateViewModel">
<ScrollViewer>
<StackPanel Margin="28,24" Spacing="14">
<TextBlock Text="🧪 Simulate Update" FontSize="20" FontWeight="Bold"/>

<!-- Test target -->
<Border Padding="16" CornerRadius="8" Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}">
<StackPanel Spacing="10">
<TextBlock Text="Test Target" FontSize="14" FontWeight="SemiBold"/>
<Grid ColumnDefinitions="Auto,*,Auto">
<TextBlock Grid.Column="0" Text="Old App Dir" VerticalAlignment="Center" Width="100"/>
<TextBox Grid.Column="1" Text="{Binding Config.AppDirectory}" IsReadOnly="True" Margin="8,0"/>
<Button Grid.Column="2" Content="📁 Select" Command="{Binding SelectAppDirCommand}" MinWidth="80"/>
</Grid>
<Grid ColumnDefinitions="Auto,*,Auto">
<TextBlock Grid.Column="0" Text="Patch Package" VerticalAlignment="Center" Width="100"/>
<TextBox Grid.Column="1" Text="{Binding Config.PatchFilePath}" IsReadOnly="True" Margin="8,0"/>
<Button Grid.Column="2" Content="📁 Select" Command="{Binding SelectPatchCommand}" MinWidth="80"/>
</Grid>
</StackPanel>
</Border>

<!-- Config -->
<Border Padding="16" CornerRadius="8" Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}">
<StackPanel Spacing="10">
<TextBlock Text="Update Config" FontSize="14" FontWeight="SemiBold"/>
<Grid ColumnDefinitions="Auto,*,Auto,*">
<TextBlock Grid.Column="0" Text="Current Ver" VerticalAlignment="Center"/>
<TextBox Grid.Column="1" Text="{Binding Config.CurrentVersion}" Margin="8,0,16,0"/>
<TextBlock Grid.Column="2" Text="Target Ver" VerticalAlignment="Center"/>
<TextBox Grid.Column="3" Text="{Binding Config.TargetVersion}" Margin="8,0"/>
</Grid>
<Grid ColumnDefinitions="Auto,*,Auto,*">
<TextBlock Grid.Column="0" Text="Platform" VerticalAlignment="Center"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding Platforms}"
SelectedItem="{Binding Config.Platform}" Margin="8,0,16,0"/>
<TextBlock Grid.Column="2" Text="AppType" VerticalAlignment="Center"/>
<ComboBox Grid.Column="3" ItemsSource="{Binding AppTypes}"
SelectedItem="{Binding Config.AppType}" Margin="8,0"/>
</Grid>
<Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Text="AppSecret" VerticalAlignment="Center"/>
<TextBox Grid.Column="1" Text="{Binding Config.AppSecretKey}" Margin="8,0"/>
</Grid>
<Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Text="Product ID" VerticalAlignment="Center"/>
<TextBox Grid.Column="1" Text="{Binding Config.ProductId}" Margin="8,0"/>
</Grid>
</StackPanel>
</Border>

<!-- Output -->
<Border Padding="16" CornerRadius="8" Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}">
<StackPanel Spacing="10">
<TextBlock Text="Output" FontSize="14" FontWeight="SemiBold"/>
<Grid ColumnDefinitions="Auto,*,Auto">
<TextBlock Grid.Column="0" Text="Simulate Dir" VerticalAlignment="Center" Width="100"/>
<TextBox Grid.Column="1" Text="{Binding Config.OutputDirectory}" IsReadOnly="True" Margin="8,0"/>
<Button Grid.Column="2" Content="📁 Select" Command="{Binding SelectOutputDirCommand}" MinWidth="80"/>
</Grid>
</StackPanel>
</Border>

<!-- Run -->
<Button Content="🚀 Start Simulation" Command="{Binding StartSimulationCommand}"
IsEnabled="{Binding !IsRunning}" Height="40" FontSize="14" HorizontalAlignment="Stretch"/>
<TextBlock Text="{Binding Status}" FontSize="13"/>

<!-- Log -->
<Border Padding="10" CornerRadius="6" Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" MaxHeight="200">
<ListBox ItemsSource="{Binding Log}" Background="Transparent" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate><TextBlock Text="{Binding}" FontSize="11" FontFamily="Consolas,monospace"/></DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>
8 changes: 8 additions & 0 deletions src/Views/SimulateView.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Avalonia.Controls;

namespace GeneralUpdate.Tools.Views;

public partial class SimulateView : UserControl
{
public SimulateView() => InitializeComponent();
}
Loading