Compare commits

..

No commits in common. "4b8a346cfb4499d3d0ecabba9b0b417a6d700725" and "4ff9b78db87f85cbd68edaf8743ceefb6bad790b" have entirely different histories.

40 changed files with 247 additions and 239 deletions

View file

@ -1,4 +1,5 @@
using System.Data.Common; using System.Data.Common;
using Npgsql;
namespace pgLabII.PgUtils.Tests.ConnectionStrings; namespace pgLabII.PgUtils.Tests.ConnectionStrings;

View file

@ -1,4 +1,5 @@
using Npgsql; using System.Collections.Generic;
using Npgsql;
using pgLabII.PgUtils.ConnectionStrings; using pgLabII.PgUtils.ConnectionStrings;
namespace pgLabII.PgUtils.Tests.ConnectionStrings; namespace pgLabII.PgUtils.Tests.ConnectionStrings;

View file

@ -1,4 +1,5 @@
using Npgsql; using System.Collections.Generic;
using Npgsql;
using pgLabII.PgUtils.ConnectionStrings; using pgLabII.PgUtils.ConnectionStrings;
namespace pgLabII.PgUtils.Tests.ConnectionStrings; namespace pgLabII.PgUtils.Tests.ConnectionStrings;

View file

@ -1,4 +1,6 @@
namespace pgLabII.PgUtils.Tests.ConnectionStrings.Util; using FluentResults;
namespace pgLabII.PgUtils.Tests.ConnectionStrings.Util;
public class UnitTestTokenizerTests public class UnitTestTokenizerTests
{ {

View file

@ -1,4 +1,8 @@
using System.Globalization; using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Npgsql; using Npgsql;
namespace pgLabII.PgUtils.ConnectionStrings; namespace pgLabII.PgUtils.ConnectionStrings;

View file

@ -1,4 +1,7 @@
using FluentResults; using System;
using System.Collections.Generic;
using System.Linq;
using FluentResults;
namespace pgLabII.PgUtils.ConnectionStrings; namespace pgLabII.PgUtils.ConnectionStrings;
@ -23,7 +26,7 @@ public sealed class ConnectionStringService : IConnectionStringService
public static ConnectionStringService CreateDefault() public static ConnectionStringService CreateDefault()
=> new(new IConnectionStringCodec[] { new LibpqCodec(), new NpgsqlCodec(), new UrlCodec(), new JdbcCodec() }); => new(new IConnectionStringCodec[] { new LibpqCodec(), new NpgsqlCodec(), new UrlCodec(), new JdbcCodec() });
public Result<ConnStringFormat> DetectFormat(string? input) public Result<ConnStringFormat> DetectFormat(string input)
{ {
if (string.IsNullOrWhiteSpace(input)) if (string.IsNullOrWhiteSpace(input))
return Result.Fail<ConnStringFormat>("Empty input"); return Result.Fail<ConnStringFormat>("Empty input");

View file

@ -1,4 +1,6 @@
namespace pgLabII.PgUtils.ConnectionStrings; using System.Collections.Generic;
namespace pgLabII.PgUtils.ConnectionStrings;
public sealed class HostEndpoint public sealed class HostEndpoint
{ {

View file

@ -8,11 +8,11 @@ namespace pgLabII.PgUtils.ConnectionStrings;
/// </summary> /// </summary>
public interface IConnectionStringService public interface IConnectionStringService
{ {
Result<ConnStringFormat> DetectFormat(string? input); Result<ConnStringFormat> DetectFormat(string input);
Result<ConnectionDescriptor> ParseToDescriptor(string input); Result<ConnectionDescriptor> ParseToDescriptor(string input);
Result<string> FormatFromDescriptor(ConnectionDescriptor descriptor, ConnStringFormat targetFormat); Result<string> FormatFromDescriptor(ConnectionDescriptor descriptor, ConnStringFormat targetFormat);
Result<string> Convert(string input, ConnStringFormat targetFormat); Result<string> Convert(string input, ConnStringFormat targetFormat);
} }

View file

@ -1,7 +1,11 @@
using System.Diagnostics.CodeAnalysis; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Text; using System.Text;
using FluentResults; using FluentResults;
using Npgsql;
namespace pgLabII.PgUtils.ConnectionStrings; namespace pgLabII.PgUtils.ConnectionStrings;

View file

@ -1,5 +1,7 @@
using System.Data.Common; using System.Collections;
using System.Data.Common;
using System.Globalization; using System.Globalization;
using System.Text;
using FluentResults; using FluentResults;
using Npgsql; using Npgsql;

View file

@ -1,4 +1,5 @@
using FluentResults; using System.Collections.ObjectModel;
using FluentResults;
using Npgsql; using Npgsql;
namespace pgLabII.PgUtils.ConnectionStrings; namespace pgLabII.PgUtils.ConnectionStrings;

View file

@ -1,5 +1,6 @@
using System.Text; using System.Text;
using FluentResults; using FluentResults;
using static System.Net.Mime.MediaTypeNames;
namespace pgLabII.PgUtils.ConnectionStrings; namespace pgLabII.PgUtils.ConnectionStrings;

View file

@ -4,6 +4,7 @@ using Avalonia.Markup.Xaml;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using pgLabII.Infra; using pgLabII.Infra;
using pgLabII.ViewModels;
using pgLabII.Views; using pgLabII.Views;
namespace pgLabII; namespace pgLabII;

View file

@ -1,4 +1,10 @@
namespace pgLabII.ConnectionManager; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace pgLabII.ConnectionManager;
internal class ConnectionRepository internal class ConnectionRepository
{ {
} }

View file

@ -1,4 +1,6 @@
using pgLabII.Model; using System.Collections.Generic;
using pgLabII.Model;
using pgLabII.Views.Controls;
namespace pgLabII; namespace pgLabII;

View file

@ -1,4 +1,5 @@
using System.Text; using System;
using System.Text;
namespace pgLabII; namespace pgLabII;

View file

@ -1,4 +1,6 @@
using System.Timers; using System;
using System.Collections.Generic;
using System.Timers;
using pgLabII.Infra; using pgLabII.Infra;
using pgLabII.Model; using pgLabII.Model;
@ -11,7 +13,7 @@ public class EditHistoryManager : IEditHistoryManager
private readonly List<EditHistoryEntry> _pendingEdits = new(); private readonly List<EditHistoryEntry> _pendingEdits = new();
private EditBuffer? _currentBuffer; private EditBuffer? _currentBuffer;
private const int BufferTimeoutMs = 500; private const int BufferTimeoutMs = 500;
private readonly System.Timers.Timer _idleFlushTimer; private readonly Timer _idleFlushTimer;
private readonly object _sync = new object(); private readonly object _sync = new object();
public EditHistoryManager(LocalDb db, Document document) public EditHistoryManager(LocalDb db, Document document)
@ -19,7 +21,7 @@ public class EditHistoryManager : IEditHistoryManager
_db = db; _db = db;
_document = document; _document = document;
_idleFlushTimer = new (BufferTimeoutMs) _idleFlushTimer = new Timer(BufferTimeoutMs)
{ {
AutoReset = false, AutoReset = false,
Enabled = false Enabled = false

View file

@ -1,4 +1,6 @@
namespace pgLabII; using System;
namespace pgLabII;
public class EditOperation public class EditOperation
{ {

View file

@ -1,4 +1,6 @@
using Avalonia.Media; using System;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
using pgLabII.Model; using pgLabII.Model;

View file

@ -39,7 +39,7 @@ namespace pgLabII.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Documents", (string)null); b.ToTable("Documents");
}); });
modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b => modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b =>
@ -69,7 +69,7 @@ namespace pgLabII.Migrations
b.HasIndex("DocumentId", "Timestamp"); b.HasIndex("DocumentId", "Timestamp");
b.ToTable("EditHistory", (string)null); b.ToTable("EditHistory");
}); });
modelBuilder.Entity("pgLabII.Model.ServerConfigurationEntity", b => modelBuilder.Entity("pgLabII.Model.ServerConfigurationEntity", b =>
@ -112,7 +112,7 @@ namespace pgLabII.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("ServerConfigurations", (string)null); b.ToTable("ServerConfigurations");
}); });
modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b => modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b =>

View file

@ -1,4 +1,7 @@
namespace pgLabII.Model; using System;
using System.Collections.Generic;
namespace pgLabII.Model;
public class Document public class Document
{ {

View file

@ -1,4 +1,6 @@
namespace pgLabII.Model; using System;
namespace pgLabII.Model;
public class EditHistoryEntry public class EditHistoryEntry
{ {

View file

@ -1,4 +1,5 @@
using Npgsql; using System;
using Npgsql;
namespace pgLabII.Model; namespace pgLabII.Model;
@ -14,5 +15,6 @@ public class ServerConfigurationEntity
public bool ColorEnabled { get; set; } = true; public bool ColorEnabled { get; set; } = true;
public int ColorArgb { get; set; } = unchecked((int)0xFF_33_33_33); // default dark gray public int ColorArgb { get; set; } = unchecked((int)0xFF_33_33_33); // default dark gray
public string UserName { get; set; } = ""; public string UserName { get; set; } = "";
public string Password { get; set; } = "";
public string Password { get; set; } = "";
} }

View file

@ -1,4 +1,5 @@
using pgLabII.Infra; using System;
using pgLabII.Infra;
namespace pgLabII.Services; namespace pgLabII.Services;

View file

@ -1,4 +1,8 @@
using pgLabII.Model; using System;
using System.Collections.Generic;
using System.Linq;
using Npgsql;
using pgLabII.Model;
using pgLabII.PgUtils.ConnectionStrings; using pgLabII.PgUtils.ConnectionStrings;
namespace pgLabII.Services; namespace pgLabII.Services;

View file

@ -1,5 +1,7 @@
using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using pgLabII.ViewModels;
namespace pgLabII; namespace pgLabII;

View file

@ -1,18 +1,35 @@
using System.Reactive; using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using Npgsql;
using pgLabII.Model; using pgLabII.Model;
using pgLabII.PgUtils.ConnectionStrings; using pgLabII.PgUtils.ConnectionStrings;
using pgLabII.Services; using pgLabII.Services;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace pgLabII.ViewModels; namespace pgLabII.ViewModels;
public partial class EditServerConfigurationViewModel : ViewModelBase public class EditServerConfigurationViewModel : ViewModelBase
{ {
private readonly IConnectionStringService _service; // Prefer new UI VM; keep old model for compatibility by wrapping when needed
// Store original for reverting changes public ServerConfigurationViewModel Configuration { get; set; }
private ServerConfigurationViewModel? _originalConfiguration;
// Connection string IO
private string _inputConnectionString = string.Empty;
public string InputConnectionString
{
get => _inputConnectionString;
set
{
this.RaiseAndSetIfChanged(ref _inputConnectionString, value);
// auto-detect when input changes and we are in Auto mode
if (ForcedFormat == ForcedFormatOption.Auto)
{
DetectFormat();
}
}
}
public enum ForcedFormatOption public enum ForcedFormatOption
{ {
@ -23,27 +40,50 @@ public partial class EditServerConfigurationViewModel : ViewModelBase
Jdbc Jdbc
} }
public Interaction<bool, bool?> CloseInteraction { get; } = new(); private ForcedFormatOption _forcedFormat = ForcedFormatOption.Auto;
// Prefer new UI VM; keep old model for compatibility by wrapping when needed public ForcedFormatOption ForcedFormat
public ServerConfigurationViewModel Configuration { get; set; } {
get => _forcedFormat;
// Connection string IO set
[Reactive] public partial string? InputConnectionString { get; set; } {
this.RaiseAndSetIfChanged(ref _forcedFormat, value);
// When forcing off Auto, clear detected label; when switching to Auto, re-detect
if (value == ForcedFormatOption.Auto)
DetectFormat();
else
DetectedFormat = null;
}
}
[Reactive] public partial ForcedFormatOption ForcedFormat { get; set; } private ConnStringFormat? _detectedFormat;
public ConnStringFormat? DetectedFormat
{
get => _detectedFormat;
private set => this.RaiseAndSetIfChanged(ref _detectedFormat, value);
}
[ObservableAsProperty] private ConnStringFormat? _detectedFormat; private ConnStringFormat _outputFormat = ConnStringFormat.Url;
[Reactive] public partial ConnStringFormat OutputFormat { get; set; } public ConnStringFormat OutputFormat
{
get => _outputFormat;
set => this.RaiseAndSetIfChanged(ref _outputFormat, value);
}
[Reactive] public partial string? OutputConnectionString { get; set; } private string _outputConnectionString = string.Empty;
public string OutputConnectionString
{
get => _outputConnectionString;
set => this.RaiseAndSetIfChanged(ref _outputConnectionString, value);
}
public ReactiveCommand<Unit, Unit> ParseConnectionStringCommand { get; } public ReactiveCommand<Unit, Unit> ParseConnectionStringCommand { get; }
public ReactiveCommand<Unit, Unit> GenerateConnectionStringCommand { get; } public ReactiveCommand<Unit, Unit> GenerateConnectionStringCommand { get; }
public ReactiveCommand<Unit, Unit> CopyOutputConnectionStringCommand { get; } public ReactiveCommand<Unit, Unit> CopyOutputConnectionStringCommand { get; }
public ReactiveCommand<Unit, Unit> SaveCommand { get; } public ReactiveCommand<Unit, Unit> SaveAndCloseCommand { get; }
public ReactiveCommand<Unit, Unit> RevertCommand { get; } public ReactiveCommand<Unit, Unit> CloseCommand { get; }
private readonly IConnectionStringService _service;
public EditServerConfigurationViewModel() public EditServerConfigurationViewModel()
{ {
@ -54,31 +94,25 @@ public partial class EditServerConfigurationViewModel : ViewModelBase
GenerateConnectionStringCommand = ReactiveCommand.Create(GenerateConnectionString); GenerateConnectionStringCommand = ReactiveCommand.Create(GenerateConnectionString);
CopyOutputConnectionStringCommand = ReactiveCommand.Create(() => { /* no-op placeholder */ }); CopyOutputConnectionStringCommand = ReactiveCommand.Create(() => { /* no-op placeholder */ });
SaveCommand = ReactiveCommand.CreateFromTask(Save); SaveAndCloseCommand = ReactiveCommand.Create(() => { });
RevertCommand = ReactiveCommand.CreateFromTask(Revert); CloseCommand = ReactiveCommand.Create(() => { });
_detectedFormatHelper = this.WhenAnyValue(x => x.ForcedFormat, x => x.InputConnectionString,
DetectFormat)
.ToProperty(this, x => x.DetectedFormat);
} }
public EditServerConfigurationViewModel(ServerConfigurationViewModel configuration) public EditServerConfigurationViewModel(ServerConfigurationViewModel configuration)
: this() : this()
{ {
Configuration = configuration; Configuration = configuration;
InitializeFromCopy();
} }
private ConnStringFormat? DetectFormat(ForcedFormatOption format, string? input) private void DetectFormat()
{ {
if (format != ForcedFormatOption.Auto) if (string.IsNullOrWhiteSpace(InputConnectionString))
return null; {
DetectedFormat = null;
if (string.IsNullOrWhiteSpace(input)) return;
return null; }
var res = _service.DetectFormat(InputConnectionString); var res = _service.DetectFormat(InputConnectionString);
return res.IsSuccess ? res.Value : null; DetectedFormat = res.IsSuccess ? res.Value : null;
} }
private void ParseConnectionString() private void ParseConnectionString()
@ -139,73 +173,4 @@ public partial class EditServerConfigurationViewModel : ViewModelBase
if (r.IsSuccess) if (r.IsSuccess)
OutputConnectionString = r.Value; OutputConnectionString = r.Value;
} }
/// <summary>
/// Creates a deep copy of the configuration for editing.
/// This ensures changes are isolated until Save is called.
/// </summary>
private void InitializeFromCopy()
{
_originalConfiguration = Configuration;
// Create a copy of the entity
var copiedEntity = new ServerConfigurationEntity
{
Id = _originalConfiguration.Entity.Id,
Name = _originalConfiguration.Entity.Name,
Host = _originalConfiguration.Entity.Host,
Port = _originalConfiguration.Entity.Port,
InitialDatabase = _originalConfiguration.Entity.InitialDatabase,
SslMode = _originalConfiguration.Entity.SslMode,
ColorEnabled = _originalConfiguration.Entity.ColorEnabled,
ColorArgb = _originalConfiguration.Entity.ColorArgb,
UserName = _originalConfiguration.Entity.UserName,
Password = _originalConfiguration.Entity.Password
};
// Create a new ViewModel wrapping the copied entity
Configuration = new ServerConfigurationViewModel(copiedEntity);
}
/// <summary>
/// Copies changes from the edited configuration back to the original.
/// </summary>
private async Task Save()
{
if (_originalConfiguration == null) return;
// Copy all properties from the edited configuration back to the original
_originalConfiguration.Name = Configuration.Name;
_originalConfiguration.Host = Configuration.Host;
_originalConfiguration.Port = Configuration.Port;
_originalConfiguration.InitialDatabase = Configuration.InitialDatabase;
_originalConfiguration.DefaultSslMode = Configuration.DefaultSslMode;
_originalConfiguration.ColorEnabled = Configuration.ColorEnabled;
_originalConfiguration.Color = Configuration.Color;
_originalConfiguration.UserName = Configuration.UserName;
_originalConfiguration.Password = Configuration.Password;
await CloseInteraction.Handle(true);
}
/// <summary>
/// Reverts to the original configuration, discarding all changes.
/// </summary>
private async Task Revert()
{
if (_originalConfiguration == null) return;
// Copy all properties from the original back to the working configuration
Configuration.Name = _originalConfiguration.Name;
Configuration.Host = _originalConfiguration.Host;
Configuration.Port = _originalConfiguration.Port;
Configuration.InitialDatabase = _originalConfiguration.InitialDatabase;
Configuration.DefaultSslMode = _originalConfiguration.DefaultSslMode;
Configuration.ColorEnabled = _originalConfiguration.ColorEnabled;
Configuration.Color = _originalConfiguration.Color;
Configuration.UserName = _originalConfiguration.UserName;
Configuration.Password = _originalConfiguration.Password;
await CloseInteraction.Handle(false);
}
} }

View file

@ -1,4 +1,8 @@
namespace pgLabII.ViewModels; 
using System;
using Microsoft.Extensions.DependencyInjection;
namespace pgLabII.ViewModels;
public class MainViewModel : ViewModelBase public class MainViewModel : ViewModelBase
{ {

View file

@ -1,5 +1,8 @@
using System.Collections.ObjectModel; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive; using System.Reactive;
using System.Threading.Tasks;
using Npgsql; using Npgsql;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.SourceGenerators; using ReactiveUI.SourceGenerators;

View file

@ -1,14 +1,14 @@
using System.Reactive; using System;
using System.Reactive;
using Avalonia.Media; using Avalonia.Media;
using Npgsql; using Npgsql;
using pgLabII.Model; using pgLabII.Model;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace pgLabII.ViewModels; namespace pgLabII.ViewModels;
// UI ViewModel that wraps the persistence entity // UI ViewModel that wraps the persistence entity
public partial class ServerConfigurationViewModel : ReactiveObject public class ServerConfigurationViewModel : ReactiveObject
{ {
private readonly ServerConfigurationEntity _entity; private readonly ServerConfigurationEntity _entity;
@ -17,7 +17,7 @@ public partial class ServerConfigurationViewModel : ReactiveObject
_entity = entity ?? throw new ArgumentNullException(nameof(entity)); _entity = entity ?? throw new ArgumentNullException(nameof(entity));
EditCommand = ReactiveCommand.Create(() => EditCommand = ReactiveCommand.Create(() =>
{ {
var window = new Views.EditServerConfigurationWindow(new(this)); var window = new Views.EditServerConfigurationWindow(new(this)) { New = false };
window.Show(); window.Show();
}); });
ExploreCommand = ReactiveCommand.Create(() => ExploreCommand = ReactiveCommand.Create(() =>
@ -25,10 +25,6 @@ public partial class ServerConfigurationViewModel : ReactiveObject
var window = new Views.SingleDatabaseWindow(entity); var window = new Views.SingleDatabaseWindow(entity);
window.Show(); window.Show();
}); });
_backgroundBrushHelper = this.WhenAnyValue(x => x.ColorEnabled, x => x.Color,
(enabled, color) => enabled ? new SolidColorBrush(color) : null)
.ToProperty(this, x => x.BackgroundBrush);
} }
public ServerConfigurationEntity Entity => _entity; public ServerConfigurationEntity Entity => _entity;
@ -72,7 +68,7 @@ public partial class ServerConfigurationViewModel : ReactiveObject
public bool ColorEnabled public bool ColorEnabled
{ {
get => _entity.ColorEnabled; get => _entity.ColorEnabled;
set { if (_entity.ColorEnabled != value) { _entity.ColorEnabled = value; this.RaisePropertyChanged(); } } set { if (_entity.ColorEnabled != value) { _entity.ColorEnabled = value; this.RaisePropertyChanged(); this.RaisePropertyChanged(nameof(BackgroundBrush)); } }
} }
public Color Color public Color Color
@ -85,12 +81,12 @@ public partial class ServerConfigurationViewModel : ReactiveObject
{ {
_entity.ColorArgb = argb; _entity.ColorArgb = argb;
this.RaisePropertyChanged(); this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(BackgroundBrush));
} }
} }
} }
//public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null; public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null;
[ObservableAsProperty] private IBrush? _backgroundBrush;
public string UserName public string UserName
{ {
@ -120,7 +116,6 @@ public partial class ServerConfigurationViewModel : ReactiveObject
} }
} }
public ReactiveCommand<Unit, Unit> EditCommand { get; } public ReactiveCommand<Unit, Unit> EditCommand { get; }
public ReactiveCommand<Unit, Unit> ExploreCommand { get; } public ReactiveCommand<Unit, Unit> ExploreCommand { get; }

View file

@ -3,14 +3,12 @@ using ReactiveUI;
using System.Reactive; using System.Reactive;
using Avalonia.Media; using Avalonia.Media;
using pgLabII.Views; using pgLabII.Views;
using ReactiveUI.SourceGenerators; using pgLabII.Model;
namespace pgLabII.ViewModels; namespace pgLabII.ViewModels;
public partial class ServerListViewModel : ViewModelBase public class ServerListViewModel : ViewModelBase
{ {
[Reactive] public partial ServerConfigurationViewModel? SelectedServerConfiguration { get; set; }
public ObservableCollection<ServerConfigurationViewModel> ServerConfigurations { get; } = public ObservableCollection<ServerConfigurationViewModel> ServerConfigurations { get; } =
[ [
new (new() new (new()
@ -21,7 +19,7 @@ public partial class ServerListViewModel : ViewModelBase
Port = 5418, Port = 5418,
InitialDatabase = "postgres", InitialDatabase = "postgres",
UserName = "postgres", UserName = "postgres",
Password = "admin" Password = "admin",
}) })
{ {
Color = Colors.Aquamarine, Color = Colors.Aquamarine,
@ -34,48 +32,22 @@ public partial class ServerListViewModel : ViewModelBase
}), }),
]; ];
public ReactiveCommand<ServerConfigurationViewModel?, Unit> ExploreServerCommand { get; } public ReactiveCommand<ServerConfigurationViewModel, Unit> RemoveServerCommand { get; }
public ReactiveCommand<ServerConfigurationViewModel?, Unit> EditServerCommand { get; }
public ReactiveCommand<ServerConfigurationViewModel?, Unit> RemoveServerCommand { get; }
public ReactiveCommand<Unit, Unit> AddServerCommand { get; } public ReactiveCommand<Unit, Unit> AddServerCommand { get; }
public ServerListViewModel() public ServerListViewModel()
{ {
ExploreServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel?, Unit>((sc) => RemoveServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel, Unit>((sc) =>
{ {
var conf = sc ?? SelectedServerConfiguration; ServerConfigurations.Remove(sc);
if (conf is null)
return Unit.Default;
var window = new Views.SingleDatabaseWindow(conf.Entity);
window.Show();
return Unit.Default;
});
EditServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel?, Unit>((sc) =>
{
var conf = sc ?? SelectedServerConfiguration;
if (conf is null)
return Unit.Default;
EditServerConfigurationWindow window = new(new(conf));
window.Show();
return Unit.Default;
});
RemoveServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel?, Unit>((sc) =>
{
var conf = sc ?? SelectedServerConfiguration;
if (conf is null)
return Unit.Default;
ServerConfigurations.Remove(conf);
return Unit.Default; return Unit.Default;
}); });
AddServerCommand = ReactiveCommand.Create(() => AddServerCommand = ReactiveCommand.Create(() =>
{ {
EditServerConfigurationWindow window = new(new()); EditServerConfigurationViewModel vm = new();
EditServerConfigurationWindow window = new() { DataContext = vm, New = true };
window.Show(); window.Show();
}); });
} }

View file

@ -1,8 +1,12 @@
using Avalonia; using System.IO;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data;
using AvaloniaEdit.Document; using AvaloniaEdit.Document;
using AvaloniaEdit.TextMate; using AvaloniaEdit.TextMate;
using TextMateSharp.Grammars; using TextMateSharp.Grammars;
using System;
using System.Reactive.Linq;
namespace pgLabII.Views.Controls; namespace pgLabII.Views.Controls;

View file

@ -1,4 +1,6 @@
using Avalonia.Controls; using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace pgLabII.Views; namespace pgLabII.Views;

View file

@ -12,7 +12,7 @@
<vm:EditServerConfigurationViewModel /> <vm:EditServerConfigurationViewModel />
</Design.DataContext> </Design.DataContext>
<Grid Margin="12" RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="*"> <Grid Margin="12" RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="*">
<!-- Basic Details --> <!-- Basic Details -->
<StackPanel Grid.Row="0" Spacing="6"> <StackPanel Grid.Row="0" Spacing="6">
<TextBlock FontWeight="Bold" Text="Details" Margin="0,0,0,4"/> <TextBlock FontWeight="Bold" Text="Details" Margin="0,0,0,4"/>
@ -93,10 +93,6 @@
<TextBox TextWrapping="Wrap" AcceptsReturn="True" MinHeight="60" IsReadOnly="True" Text="{Binding OutputConnectionString}"/> <TextBox TextWrapping="Wrap" AcceptsReturn="True" MinHeight="60" IsReadOnly="True" Text="{Binding OutputConnectionString}"/>
</StackPanel> </StackPanel>
<!-- Buttons Row --> <!-- Spacer / Future buttons row could go here -->
<StackPanel Grid.Row="3" Orientation="Horizontal" Spacing="8" Margin="0,12,0,0" HorizontalAlignment="Right">
<Button Content="Revert" Command="{Binding RevertCommand}" MinWidth="80"/>
<Button Content="Save" Command="{Binding SaveCommand}" MinWidth="80"/>
</StackPanel>
</Grid> </Grid>
</Window> </Window>

View file

@ -1,36 +1,26 @@
using System.Reactive.Disposables; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using pgLabII.ViewModels; using pgLabII.ViewModels;
using ReactiveUI;
namespace pgLabII.Views; namespace pgLabII.Views;
public partial class EditServerConfigurationWindow : ReactiveWindow<EditServerConfigurationWindow> public partial class EditServerConfigurationWindow : Window
{ {
private readonly EditServerConfigurationViewModel _viewModel;
public EditServerConfigurationWindow() public EditServerConfigurationWindow()
: this(null) : this(null)
{ {
} }
public EditServerConfigurationWindow(EditServerConfigurationViewModel? viewModel) public EditServerConfigurationWindow(EditServerConfigurationViewModel? viewModel)
{ {
InitializeComponent(); InitializeComponent();
DataContext = _viewModel = viewModel ?? new EditServerConfigurationViewModel( DataContext = viewModel ?? new EditServerConfigurationViewModel(
new(new())); new(new()));
this.WhenActivated(disposables =>
{
// Subscribe to the CloseInteraction
_viewModel!.CloseInteraction.RegisterHandler(interaction =>
{
//DialogResult = interaction.Input; // true/false/null
Close();
interaction.SetOutput(true);
}).DisposeWith(disposables);
});
} }
public bool New { get; set; }
} }

View file

@ -15,35 +15,56 @@
</Design.DataContext> </Design.DataContext>
<StackPanel x:Name="ServerList" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <StackPanel x:Name="ServerList" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal" <Grid ColumnDefinitions="Auto,*,Auto">
HorizontalAlignment="Stretch" <Button Grid.Column="2" Content="+" Command="{Binding AddServerCommand}"/>
VerticalAlignment="Top"> </Grid>
<Button Content="New" <ListBox x:Name="Servers" ItemsSource="{Binding ServerConfigurations}">
Command="{Binding AddServerCommand}"/> <ListBox.ItemTemplate>
<Button Content="Edit" <DataTemplate>
Command="{Binding EditServerCommand}"/> <Border BorderBrush="{Binding BackgroundBrush}" BorderThickness="2">
<Button Content="Open" <Grid ColumnDefinitions="*,*">
Command="{Binding ExploreServerCommand}"/> <StackPanel
</StackPanel> Orientation="Vertical"
HorizontalAlignment="Stretch"
<DataGrid ItemsSource="{Binding ServerConfigurations}" Grid.Row="0"
SelectedItem="{Binding SelectedServerConfiguration, Mode=TwoWay}" Grid.Column="0">
HorizontalAlignment="Stretch" <TextBlock Text="{Binding Name}" FontSize="18" />
VerticalAlignment="Stretch" <StackPanel Orientation="Horizontal">
AutoGenerateColumns="False" <TextBlock Text="{Binding Host}" />
IsReadOnly="True" <TextBlock Text=":" />
GridLinesVisibility="All" <TextBlock Text="{Binding Port}" />
BorderThickness="1"> <TextBlock Text="/" />
<DataGrid.Columns> <TextBlock Text="{Binding InitialDatabase}" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/> </StackPanel>
<DataGridTextColumn Header="Host" Binding="{Binding Host}" />
<DataGridTextColumn Header="Port" Binding="{Binding Port}" />
<DataGridTextColumn Header="Db" Binding="{Binding InitialDatabase}" />
<DataGridTextColumn Header="User" Binding="{Binding UserName}" />
<DataGridTextColumn Header="SSL" Binding="{Binding DefaultSslMode}" />
</DataGrid.Columns>
</DataGrid> </StackPanel>
<StackPanel
Orientation="Horizontal"
HorizontalAlignment="Right"
Grid.Row="0"
Grid.Column="1">
<Button Command="{Binding ExploreCommand}">
DB
</Button>
<Button>Server</Button>
<Button Content="...">
<Button.Flyout>
<MenuFlyout>
<MenuItem Header="Edit" Command="{Binding EditCommand}" />
<Separator />
<MenuItem Header="Remove"
Command="{Binding #ServerList.((vm:ServerListViewModel)DataContext).RemoveServerCommand}"
CommandParameter="{Binding .}"
Foreground="Crimson" />
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View file

@ -1,4 +1,5 @@
using Avalonia.Controls; using Avalonia.Controls;
using pgLabII.ViewModels;
namespace pgLabII.Views; namespace pgLabII.Views;

View file

@ -1,4 +1,6 @@
using Avalonia.Controls; using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace pgLabII.Views; namespace pgLabII.Views;

View file

@ -1,4 +1,6 @@
using Avalonia.Controls; using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using pgLabII.Model; using pgLabII.Model;
using pgLabII.ViewModels; using pgLabII.ViewModels;

View file

@ -3,7 +3,6 @@
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup> </PropertyGroup>