Применение к сессии, USE заменён на checkbox
This commit is contained in:
10
README.md
10
README.md
@@ -71,14 +71,14 @@ dotnet run --project src/Sms.TaskTwo.Avalonia/Sms.TaskTwo.Avalonia.csproj
|
|||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
- Чтение/запись: `EnvironmentVariableTarget.User` (реестр `HKEY_CURRENT_USER\Environment`).
|
- Чтение/запись: реестр `HKEY_CURRENT_USER\Environment`.
|
||||||
- После записи отправляется `WM_SETTINGCHANGE`, чтобы обновить env в уже запущенных GUI-приложениях.
|
- **Применить к сессии** (`ReloadEnvironment`): обновляет env текущего процесса и рассылает `WM_SETTINGCHANGE` для других GUI-приложений.
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
- Запись в `~/.config/environment.d/99-sms-task-two.conf` (формат systemd `KEY=value`, значения с пробелами в кавычках).
|
- Запись в `~/.config/environment.d/` (systemd `KEY=value`).
|
||||||
- Чтение: merge всех `*.conf` в `environment.d`, затем fallback на env текущего процесса.
|
- **Применить к сессии** (`ReloadEnvironment`): текущий процесс, `systemctl --user set-environment`, при наличии — `dbus-update-environment`.
|
||||||
- Для новых login-сессий может потребоваться перелогин или `systemctl --user import-environment` — ограничение systemd, не ошибка приложения.
|
- Новые login-сессии могут потребовать перелогин — ограничение systemd.
|
||||||
|
|
||||||
## Предположения (ТЗ)
|
## Предположения (ТЗ)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
using Sms.Environment;
|
using Sms.Environment;
|
||||||
|
|
||||||
namespace Sms.Environment.Linux;
|
namespace Sms.Environment.Linux;
|
||||||
@@ -5,6 +6,7 @@ namespace Sms.Environment.Linux;
|
|||||||
public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
||||||
{
|
{
|
||||||
private const string ManagedFileName = "99-sms-task-two.conf";
|
private const string ManagedFileName = "99-sms-task-two.conf";
|
||||||
|
private const int SystemctlBatchSize = 32;
|
||||||
|
|
||||||
private readonly string _managedFilePath;
|
private readonly string _managedFilePath;
|
||||||
private readonly string _environmentDirectory;
|
private readonly string _environmentDirectory;
|
||||||
@@ -119,6 +121,7 @@ public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
}
|
}
|
||||||
|
|
||||||
System.Environment.SetEnvironmentVariable(name, null);
|
System.Environment.SetEnvironmentVariable(name, null);
|
||||||
|
TryRunSystemctl(["--user", "unset-environment", name], out _);
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
|
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
|
||||||
{
|
{
|
||||||
@@ -128,6 +131,142 @@ public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EnvironmentReloadResult ReloadEnvironment()
|
||||||
|
{
|
||||||
|
var variables = GetUserPersistedEnvironment();
|
||||||
|
ApplyToCurrentProcess(variables);
|
||||||
|
|
||||||
|
if (variables.Count == 0)
|
||||||
|
{
|
||||||
|
return new EnvironmentReloadResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = "Пользовательских переменных для применения нет.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages = new List<string>();
|
||||||
|
if (TryApplyViaSystemctl(variables, out var systemctlError))
|
||||||
|
{
|
||||||
|
messages.Add("systemd user manager обновлён (systemctl --user set-environment).");
|
||||||
|
}
|
||||||
|
else if (systemctlError is not null)
|
||||||
|
{
|
||||||
|
messages.Add($"systemctl: {systemctlError}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryApplyViaDbusUpdateEnvironment(variables, out var dbusError))
|
||||||
|
{
|
||||||
|
messages.Add("D-Bus session обновлён (dbus-update-environment).");
|
||||||
|
}
|
||||||
|
else if (dbusError is not null)
|
||||||
|
{
|
||||||
|
messages.Add($"dbus-update-environment: {dbusError}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var processApplied = messages.Count > 0 || variables.Count > 0;
|
||||||
|
return new EnvironmentReloadResult
|
||||||
|
{
|
||||||
|
Success = processApplied,
|
||||||
|
Message = processApplied
|
||||||
|
? $"Применено {variables.Count} переменных к текущему процессу. {string.Join(' ', messages)}"
|
||||||
|
: "Не удалось применить переменные к сессии.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyToCurrentProcess(IReadOnlyDictionary<string, string> variables)
|
||||||
|
{
|
||||||
|
foreach (var pair in variables)
|
||||||
|
{
|
||||||
|
System.Environment.SetEnvironmentVariable(pair.Key, pair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryApplyViaSystemctl(
|
||||||
|
IReadOnlyDictionary<string, string> variables,
|
||||||
|
out string? errorMessage)
|
||||||
|
{
|
||||||
|
errorMessage = null;
|
||||||
|
var pairs = variables.ToList();
|
||||||
|
for (var offset = 0; offset < pairs.Count; offset += SystemctlBatchSize)
|
||||||
|
{
|
||||||
|
var arguments = new List<string> { "--user", "set-environment" };
|
||||||
|
foreach (var pair in pairs.Skip(offset).Take(SystemctlBatchSize))
|
||||||
|
{
|
||||||
|
arguments.Add($"{pair.Key}={pair.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryRunSystemctl(arguments, out errorMessage))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryApplyViaDbusUpdateEnvironment(
|
||||||
|
IReadOnlyDictionary<string, string> variables,
|
||||||
|
out string? errorMessage)
|
||||||
|
{
|
||||||
|
var arguments = new List<string>();
|
||||||
|
foreach (var pair in variables)
|
||||||
|
{
|
||||||
|
arguments.Add($"{pair.Key}={pair.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryRunCommand("dbus-update-environment", arguments, out errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryRunSystemctl(IReadOnlyList<string> arguments, out string? errorMessage) =>
|
||||||
|
TryRunCommand("systemctl", arguments, out errorMessage);
|
||||||
|
|
||||||
|
private static bool TryRunCommand(
|
||||||
|
string fileName,
|
||||||
|
IReadOnlyList<string> arguments,
|
||||||
|
out string? errorMessage)
|
||||||
|
{
|
||||||
|
errorMessage = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = fileName,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
};
|
||||||
|
foreach (var argument in arguments)
|
||||||
|
{
|
||||||
|
startInfo.ArgumentList.Add(argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var process = Process.Start(startInfo);
|
||||||
|
if (process is null)
|
||||||
|
{
|
||||||
|
errorMessage = "не удалось запустить процесс";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stderr = process.StandardError.ReadToEnd();
|
||||||
|
process.WaitForExit(5000);
|
||||||
|
if (process.ExitCode == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessage = string.IsNullOrWhiteSpace(stderr)
|
||||||
|
? $"код выхода {process.ExitCode}"
|
||||||
|
: stderr.Trim();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = ex.Message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<string, string> LoadManagedFile()
|
private Dictionary<string, string> LoadManagedFile()
|
||||||
{
|
{
|
||||||
if (!File.Exists(_managedFilePath))
|
if (!File.Exists(_managedFilePath))
|
||||||
|
|||||||
@@ -83,6 +83,25 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
BroadcastEnvironmentChange();
|
BroadcastEnvironmentChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EnvironmentReloadResult ReloadEnvironment()
|
||||||
|
{
|
||||||
|
var variables = GetUserPersistedEnvironment();
|
||||||
|
foreach (var pair in variables)
|
||||||
|
{
|
||||||
|
System.Environment.SetEnvironmentVariable(pair.Key, pair.Value, EnvironmentVariableTarget.Process);
|
||||||
|
}
|
||||||
|
|
||||||
|
BroadcastEnvironmentChange();
|
||||||
|
|
||||||
|
return new EnvironmentReloadResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = variables.Count == 0
|
||||||
|
? "Пользовательских переменных для применения нет."
|
||||||
|
: $"Применено {variables.Count} переменных к текущему процессу и отправлено WM_SETTINGCHANGE для других приложений.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static Dictionary<string, string> ToDictionary(System.Collections.IDictionary source)
|
private static Dictionary<string, string> ToDictionary(System.Collections.IDictionary source)
|
||||||
{
|
{
|
||||||
var result = new Dictionary<string, string>(StringComparer.Ordinal);
|
var result = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
|||||||
8
src/Sms.Environment/EnvironmentReloadResult.cs
Normal file
8
src/Sms.Environment/EnvironmentReloadResult.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Sms.Environment;
|
||||||
|
|
||||||
|
public sealed class EnvironmentReloadResult
|
||||||
|
{
|
||||||
|
public required bool Success { get; init; }
|
||||||
|
|
||||||
|
public required string Message { get; init; }
|
||||||
|
}
|
||||||
@@ -19,4 +19,6 @@ public interface IEnvironmentVariableStore
|
|||||||
bool IsPersistedInUserStore(string name);
|
bool IsPersistedInUserStore(string name);
|
||||||
|
|
||||||
void RemoveFromUserStore(string name);
|
void RemoveFromUserStore(string name);
|
||||||
|
|
||||||
|
EnvironmentReloadResult ReloadEnvironment();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
<Grid Grid.Row="1"
|
<Grid Grid.Row="1"
|
||||||
Margin="12,8,12,0"
|
Margin="12,8,12,0"
|
||||||
ColumnDefinitions="*,Auto"
|
ColumnDefinitions="*,Auto,Auto"
|
||||||
ColumnSpacing="8">
|
ColumnSpacing="8">
|
||||||
<CheckBox Content="Отображать все переменные"
|
<CheckBox Content="Отображать все переменные"
|
||||||
IsChecked="{Binding ShowAllVariables}"
|
IsChecked="{Binding ShowAllVariables}"
|
||||||
@@ -52,6 +52,10 @@
|
|||||||
Content="Обновить"
|
Content="Обновить"
|
||||||
Command="{Binding RefreshCommand}"
|
Command="{Binding RefreshCommand}"
|
||||||
MinWidth="100" />
|
MinWidth="100" />
|
||||||
|
<Button Grid.Column="2"
|
||||||
|
Content="Применить к сессии"
|
||||||
|
Command="{Binding ReloadEnvironmentCommand}"
|
||||||
|
MinWidth="140" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Row="2"
|
<Grid Grid.Row="2"
|
||||||
@@ -73,11 +77,17 @@
|
|||||||
MinWidth="100" />
|
MinWidth="100" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TextBlock Grid.Row="3"
|
<StackPanel Grid.Row="3"
|
||||||
Margin="12,4,12,0"
|
Margin="12,4,12,0"
|
||||||
Foreground="#C62828"
|
Spacing="2">
|
||||||
|
<TextBlock Foreground="#C62828"
|
||||||
Text="{Binding AddVariableError}"
|
Text="{Binding AddVariableError}"
|
||||||
IsVisible="{Binding AddVariableError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
IsVisible="{Binding AddVariableError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
|
<TextBlock Foreground="#2E7D32"
|
||||||
|
Text="{Binding ReloadEnvironmentMessage}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
IsVisible="{Binding ReloadEnvironmentMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<DataGrid x:Name="VariablesGrid"
|
<DataGrid x:Name="VariablesGrid"
|
||||||
Grid.Row="4"
|
Grid.Row="4"
|
||||||
@@ -93,10 +103,17 @@
|
|||||||
BorderBrush="#B0B0B0"
|
BorderBrush="#B0B0B0"
|
||||||
LoadingRow="OnLoadingRow">
|
LoadingRow="OnLoadingRow">
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn Header=""
|
<DataGridTemplateColumn Header=""
|
||||||
Binding="{Binding UserStoreBadge}"
|
Width="44">
|
||||||
IsReadOnly="True"
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
Width="56" />
|
<DataTemplate x:DataType="vm:EnvironmentVariableRowViewModel">
|
||||||
|
<CheckBox IsChecked="{Binding UseUserStore, Mode=TwoWay}"
|
||||||
|
IsEnabled="{Binding CanChangeUserStore}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
<DataGridTextColumn Header="Поле"
|
<DataGridTextColumn Header="Поле"
|
||||||
Binding="{Binding Field}"
|
Binding="{Binding Field}"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
@@ -115,17 +132,6 @@
|
|||||||
Binding="{Binding Comment, Mode=TwoWay}"
|
Binding="{Binding Comment, Mode=TwoWay}"
|
||||||
Width="240"
|
Width="240"
|
||||||
MinWidth="180" />
|
MinWidth="180" />
|
||||||
<DataGridTemplateColumn Header=""
|
|
||||||
Width="80">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:EnvironmentVariableRowViewModel">
|
|
||||||
<Button Content="Удалить"
|
|
||||||
Command="{Binding DeleteFromUserStoreCommand}"
|
|
||||||
Padding="6,2"
|
|
||||||
FontSize="11" />
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public partial class MainWindow : Window
|
|||||||
gridRow.Classes.Add("custom");
|
gridRow.Classes.Add("custom");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row.IsPersistedInUserStore)
|
if (row.UseUserStore)
|
||||||
{
|
{
|
||||||
gridRow.Classes.Add("userStore");
|
gridRow.Classes.Add("userStore");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,14 @@ public sealed class EnvironmentVariablesService
|
|||||||
public string? GetProcessValue(string name) =>
|
public string? GetProcessValue(string name) =>
|
||||||
_store.GetProcessEnvironment().TryGetValue(name, out var value) ? value : null;
|
_store.GetProcessEnvironment().TryGetValue(name, out var value) ? value : null;
|
||||||
|
|
||||||
|
public EnvironmentReloadResult ReloadEnvironment()
|
||||||
|
{
|
||||||
|
var result = _store.ReloadEnvironment();
|
||||||
|
_log.WriteLine(
|
||||||
|
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Reload environment: success={result.Success}; {result.Message}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public void SaveValue(string name, string value)
|
public void SaveValue(string name, string value)
|
||||||
{
|
{
|
||||||
var previous = _store.GetUserPersistedValue(name);
|
var previous = _store.GetUserPersistedValue(name);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using Sms.TaskTwo.Core.Models;
|
using Sms.TaskTwo.Core.Models;
|
||||||
using Sms.TaskTwo.Core.Services;
|
using Sms.TaskTwo.Core.Services;
|
||||||
|
|
||||||
@@ -8,14 +7,19 @@ namespace Sms.TaskTwo.ViewModels;
|
|||||||
public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly EnvironmentVariablesService _service;
|
private readonly EnvironmentVariablesService _service;
|
||||||
|
private readonly Action? _onCustomRemoved;
|
||||||
private bool _isLoading;
|
private bool _isLoading;
|
||||||
|
|
||||||
public EnvironmentVariableRowViewModel(EnvironmentVariableRow row, EnvironmentVariablesService service)
|
public EnvironmentVariableRowViewModel(
|
||||||
|
EnvironmentVariableRow row,
|
||||||
|
EnvironmentVariablesService service,
|
||||||
|
Action? onCustomRemoved = null)
|
||||||
{
|
{
|
||||||
Field = row.Field;
|
Field = row.Field;
|
||||||
IsFromAppSettings = row.IsFromAppSettings;
|
IsFromAppSettings = row.IsFromAppSettings;
|
||||||
IsCustom = row.IsCustom;
|
IsCustom = row.IsCustom;
|
||||||
_service = service;
|
_service = service;
|
||||||
|
_onCustomRemoved = onCustomRemoved;
|
||||||
ApplySnapshot(row, suppressSave: true);
|
ApplySnapshot(row, suppressSave: true);
|
||||||
RefreshActualValue(_service.GetProcessValue(Field));
|
RefreshActualValue(_service.GetProcessValue(Field));
|
||||||
}
|
}
|
||||||
@@ -26,8 +30,10 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
|
|
||||||
public bool IsCustom { get; }
|
public bool IsCustom { get; }
|
||||||
|
|
||||||
|
public bool CanChangeUserStore => !IsFromAppSettings;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isPersistedInUserStore;
|
private bool _useUserStore;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _actualValue = string.Empty;
|
private string _actualValue = string.Empty;
|
||||||
@@ -35,36 +41,60 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
public string ActualValueDisplay =>
|
public string ActualValueDisplay =>
|
||||||
string.IsNullOrEmpty(ActualValue) ? "<нет>" : ActualValue;
|
string.IsNullOrEmpty(ActualValue) ? "<нет>" : ActualValue;
|
||||||
|
|
||||||
public string UserStoreBadge => IsPersistedInUserStore ? "USER" : string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _requiredValue = string.Empty;
|
private string _requiredValue = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _comment = string.Empty;
|
private string _comment = string.Empty;
|
||||||
|
|
||||||
partial void OnIsPersistedInUserStoreChanged(bool value)
|
partial void OnUseUserStoreChanged(bool value)
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(UserStoreBadge));
|
|
||||||
DeleteFromUserStoreCommand.NotifyCanExecuteChanged();
|
|
||||||
RowAppearanceChanged?.Invoke(this, EventArgs.Empty);
|
RowAppearanceChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
|
if (_isLoading)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsFromAppSettings && !value)
|
||||||
|
{
|
||||||
|
BeginLoad();
|
||||||
|
UseUserStore = true;
|
||||||
|
EndLoad();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
_service.SaveValue(Field, RequiredValue);
|
||||||
|
RefreshActualValue(_service.GetProcessValue(Field));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsCustom)
|
||||||
|
{
|
||||||
|
_service.RemoveVariable(Field);
|
||||||
|
_onCustomRemoved?.Invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_service.DeleteFromUserStore(Field);
|
||||||
|
BeginLoad();
|
||||||
|
RequiredValue = _service.GetDisplayValue(Field);
|
||||||
|
EndLoad();
|
||||||
|
RefreshActualValue(_service.GetProcessValue(Field));
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnActualValueChanged(string value) => OnPropertyChanged(nameof(ActualValueDisplay));
|
partial void OnActualValueChanged(string value) => OnPropertyChanged(nameof(ActualValueDisplay));
|
||||||
|
|
||||||
partial void OnRequiredValueChanged(string value)
|
partial void OnRequiredValueChanged(string value)
|
||||||
{
|
{
|
||||||
if (_isLoading)
|
if (_isLoading || !UseUserStore)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_service.SaveValue(Field, value);
|
_service.SaveValue(Field, value);
|
||||||
if (!IsPersistedInUserStore)
|
|
||||||
{
|
|
||||||
IsPersistedInUserStore = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshActualValue(_service.GetProcessValue(Field));
|
RefreshActualValue(_service.GetProcessValue(Field));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +109,7 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
|
|
||||||
RequiredValue = row.Value;
|
RequiredValue = row.Value;
|
||||||
Comment = row.Comment;
|
Comment = row.Comment;
|
||||||
IsPersistedInUserStore = row.IsPersistedInUserStore;
|
UseUserStore = row.IsFromAppSettings || row.IsPersistedInUserStore;
|
||||||
|
|
||||||
if (suppressSave)
|
if (suppressSave)
|
||||||
{
|
{
|
||||||
@@ -105,26 +135,4 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
|
|
||||||
_service.SaveComment(Field, value);
|
_service.SaveComment(Field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanDeleteFromUserStore))]
|
|
||||||
private void DeleteFromUserStore()
|
|
||||||
{
|
|
||||||
if (IsCustom)
|
|
||||||
{
|
|
||||||
_service.RemoveVariable(Field);
|
|
||||||
Removed?.Invoke(this, EventArgs.Empty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_service.DeleteFromUserStore(Field);
|
|
||||||
BeginLoad();
|
|
||||||
IsPersistedInUserStore = false;
|
|
||||||
RequiredValue = _service.GetDisplayValue(Field);
|
|
||||||
EndLoad();
|
|
||||||
RefreshActualValue(_service.GetProcessValue(Field));
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler? Removed;
|
|
||||||
|
|
||||||
private bool CanDeleteFromUserStore() => IsPersistedInUserStore || IsCustom;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Sms.TaskTwo.Core.Models;
|
||||||
using Sms.TaskTwo.Core.Services;
|
using Sms.TaskTwo.Core.Services;
|
||||||
|
|
||||||
namespace Sms.TaskTwo.ViewModels;
|
namespace Sms.TaskTwo.ViewModels;
|
||||||
@@ -31,6 +32,9 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _addVariableError;
|
private string? _addVariableError;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _reloadEnvironmentMessage;
|
||||||
|
|
||||||
partial void OnShowAllVariablesChanged(bool value) => SyncRowsFromService();
|
partial void OnShowAllVariablesChanged(bool value) => SyncRowsFromService();
|
||||||
|
|
||||||
partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged();
|
partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged();
|
||||||
@@ -42,6 +46,14 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
RefreshProcessStates();
|
RefreshProcessStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void ReloadEnvironment()
|
||||||
|
{
|
||||||
|
var result = _service.ReloadEnvironment();
|
||||||
|
ReloadEnvironmentMessage = result.Message;
|
||||||
|
RefreshProcessStates();
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanAddVariable))]
|
[RelayCommand(CanExecute = nameof(CanAddVariable))]
|
||||||
private void AddVariable()
|
private void AddVariable()
|
||||||
{
|
{
|
||||||
@@ -57,18 +69,23 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
NewVariableValue = string.Empty;
|
NewVariableValue = string.Empty;
|
||||||
|
|
||||||
var row = _service.GetRowSnapshot(name);
|
var row = _service.GetRowSnapshot(name);
|
||||||
var viewModel = new EnvironmentVariableRowViewModel(row, _service);
|
Rows.Insert(FindInsertIndexForCustomVariable(), CreateRowViewModel(row));
|
||||||
viewModel.Removed += OnRowRemoved;
|
|
||||||
Rows.Insert(FindInsertIndexForCustomVariable(), viewModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRowRemoved(object? sender, EventArgs e)
|
private EnvironmentVariableRowViewModel CreateRowViewModel(EnvironmentVariableRow row)
|
||||||
{
|
{
|
||||||
if (sender is EnvironmentVariableRowViewModel row)
|
EnvironmentVariableRowViewModel? viewModel = null;
|
||||||
|
viewModel = new EnvironmentVariableRowViewModel(
|
||||||
|
row,
|
||||||
|
_service,
|
||||||
|
onCustomRemoved: () =>
|
||||||
{
|
{
|
||||||
row.Removed -= OnRowRemoved;
|
if (viewModel is not null)
|
||||||
Rows.Remove(row);
|
{
|
||||||
|
Rows.Remove(viewModel);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int FindInsertIndexForCustomVariable()
|
private int FindInsertIndexForCustomVariable()
|
||||||
@@ -93,10 +110,8 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
|
|
||||||
for (var index = Rows.Count - 1; index >= 0; index--)
|
for (var index = Rows.Count - 1; index >= 0; index--)
|
||||||
{
|
{
|
||||||
var row = Rows[index];
|
if (data.All(item => item.Field != Rows[index].Field))
|
||||||
if (data.All(item => item.Field != row.Field))
|
|
||||||
{
|
{
|
||||||
row.Removed -= OnRowRemoved;
|
|
||||||
Rows.RemoveAt(index);
|
Rows.RemoveAt(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,8 +128,7 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
viewModel = new EnvironmentVariableRowViewModel(row, _service);
|
viewModel = CreateRowViewModel(row);
|
||||||
viewModel.Removed += OnRowRemoved;
|
|
||||||
existingByField[row.Field] = viewModel;
|
existingByField[row.Field] = viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user