OF-DL/OF DL.Gui/ViewModels/CreatorConfigModalViewModel.cs

306 lines
12 KiB
C#

using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using Avalonia;
using Avalonia.Styling;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using OF_DL.Models.Config;
using Serilog;
namespace OF_DL.Gui.ViewModels;
public partial class CreatorConfigModalViewModel : ViewModelBase
{
private static readonly Regex s_fileNameVariableRegex = new(@"\{([^{}]+)\}", RegexOptions.Compiled);
private static readonly string[] s_postFileNameVariables = ["id", "postedAt", "mediaId", "mediaCreatedAt", "filename", "username", "text"];
private static readonly string[] s_messageFileNameVariables = ["id", "createdAt", "mediaId", "mediaCreatedAt", "filename", "username", "text"];
private readonly Action<bool> _onClose;
private readonly Func<bool> _isUsernameDuplicate;
[ObservableProperty] private bool _isOpen;
[ObservableProperty] private bool _isEditMode;
[ObservableProperty] private string _originalUsername = string.Empty;
[ObservableProperty] private string _username = string.Empty;
[ObservableProperty] private string _usernameError = string.Empty;
[ObservableProperty] private string _paidPostFileNameFormat = string.Empty;
[ObservableProperty] private string _postFileNameFormat = string.Empty;
[ObservableProperty] private string _paidMessageFileNameFormat = string.Empty;
[ObservableProperty] private string _messageFileNameFormat = string.Empty;
[ObservableProperty] private string _selectedPaidPostVariable = string.Empty;
[ObservableProperty] private string _selectedPostVariable = string.Empty;
[ObservableProperty] private string _selectedPaidMessageVariable = string.Empty;
[ObservableProperty] private string _selectedMessageVariable = string.Empty;
[ObservableProperty] private string _unknownPaidPostVariablesMessage = string.Empty;
[ObservableProperty] private string _unknownPostVariablesMessage = string.Empty;
[ObservableProperty] private string _unknownPaidMessageVariablesMessage = string.Empty;
[ObservableProperty] private string _unknownMessageVariablesMessage = string.Empty;
public ObservableCollection<string> AvailableUsers { get; }
public ObservableCollection<string> PaidPostVariables { get; } = [];
public ObservableCollection<string> PostVariables { get; } = [];
public ObservableCollection<string> PaidMessageVariables { get; } = [];
public ObservableCollection<string> MessageVariables { get; } = [];
public ObservableCollection<FileNameFormatSegmentViewModel> PaidPostSegments { get; } = [];
public ObservableCollection<FileNameFormatSegmentViewModel> PostSegments { get; } = [];
public ObservableCollection<FileNameFormatSegmentViewModel> PaidMessageSegments { get; } = [];
public ObservableCollection<FileNameFormatSegmentViewModel> MessageSegments { get; } = [];
public bool HasUsernameError => !string.IsNullOrWhiteSpace(UsernameError);
public bool HasUnknownPaidPostVariables => !string.IsNullOrWhiteSpace(UnknownPaidPostVariablesMessage);
public bool HasUnknownPostVariables => !string.IsNullOrWhiteSpace(UnknownPostVariablesMessage);
public bool HasUnknownPaidMessageVariables => !string.IsNullOrWhiteSpace(UnknownPaidMessageVariablesMessage);
public bool HasUnknownMessageVariables => !string.IsNullOrWhiteSpace(UnknownMessageVariablesMessage);
public string DialogTitle => IsEditMode ? "Edit Creator Config" : "Add Creator Config";
public CreatorConfigModalViewModel(IEnumerable<string> availableUsers, Action<bool> onClose, Func<bool> isUsernameDuplicate)
{
AvailableUsers = new ObservableCollection<string>(availableUsers);
_onClose = onClose;
_isUsernameDuplicate = isUsernameDuplicate;
foreach (string variable in s_postFileNameVariables)
{
PaidPostVariables.Add(variable);
PostVariables.Add(variable);
}
foreach (string variable in s_messageFileNameVariables)
{
PaidMessageVariables.Add(variable);
MessageVariables.Add(variable);
}
SelectedPaidPostVariable = PaidPostVariables.FirstOrDefault() ?? string.Empty;
SelectedPostVariable = PostVariables.FirstOrDefault() ?? string.Empty;
SelectedPaidMessageVariable = PaidMessageVariables.FirstOrDefault() ?? string.Empty;
SelectedMessageVariable = MessageVariables.FirstOrDefault() ?? string.Empty;
}
public void OpenForAdd()
{
Log.Information("=== OpenForAdd called ===");
IsEditMode = false;
OriginalUsername = string.Empty;
Username = string.Empty;
UsernameError = string.Empty;
PaidPostFileNameFormat = string.Empty;
PostFileNameFormat = string.Empty;
PaidMessageFileNameFormat = string.Empty;
MessageFileNameFormat = string.Empty;
ClearAllPreviews();
Log.Information("About to set IsOpen = true");
IsOpen = true;
Log.Information("=== OpenForAdd: IsOpen is now {IsOpen} ===", IsOpen);
}
public void OpenForEdit(string username, CreatorConfig config)
{
IsEditMode = true;
OriginalUsername = username;
Username = username;
UsernameError = string.Empty;
PaidPostFileNameFormat = config.PaidPostFileNameFormat ?? string.Empty;
PostFileNameFormat = config.PostFileNameFormat ?? string.Empty;
PaidMessageFileNameFormat = config.PaidMessageFileNameFormat ?? string.Empty;
MessageFileNameFormat = config.MessageFileNameFormat ?? string.Empty;
UpdateAllPreviews();
IsOpen = true;
}
public (string Username, CreatorConfig Config)? GetResult()
{
if (!Validate())
{
return null;
}
CreatorConfig config = new()
{
PaidPostFileNameFormat = string.IsNullOrWhiteSpace(PaidPostFileNameFormat) ? null : PaidPostFileNameFormat,
PostFileNameFormat = string.IsNullOrWhiteSpace(PostFileNameFormat) ? null : PostFileNameFormat,
PaidMessageFileNameFormat = string.IsNullOrWhiteSpace(PaidMessageFileNameFormat) ? null : PaidMessageFileNameFormat,
MessageFileNameFormat = string.IsNullOrWhiteSpace(MessageFileNameFormat) ? null : MessageFileNameFormat
};
return (Username.Trim(), config);
}
[RelayCommand]
private void InsertPaidPostVariable()
{
if (!string.IsNullOrWhiteSpace(SelectedPaidPostVariable))
{
PaidPostFileNameFormat += $"{{{SelectedPaidPostVariable}}}";
}
}
[RelayCommand]
private void InsertPostVariable()
{
if (!string.IsNullOrWhiteSpace(SelectedPostVariable))
{
PostFileNameFormat += $"{{{SelectedPostVariable}}}";
}
}
[RelayCommand]
private void InsertPaidMessageVariable()
{
if (!string.IsNullOrWhiteSpace(SelectedPaidMessageVariable))
{
PaidMessageFileNameFormat += $"{{{SelectedPaidMessageVariable}}}";
}
}
[RelayCommand]
private void InsertMessageVariable()
{
if (!string.IsNullOrWhiteSpace(SelectedMessageVariable))
{
MessageFileNameFormat += $"{{{SelectedMessageVariable}}}";
}
}
[RelayCommand]
private void Confirm()
{
if (Validate())
{
_onClose(true);
}
}
[RelayCommand]
private void Cancel()
{
_onClose(false);
}
partial void OnIsOpenChanged(bool value)
{
Log.Information("*** IsOpen property changed to: {Value} ***", value);
}
partial void OnPaidPostFileNameFormatChanged(string value) => UpdatePaidPostPreview();
partial void OnPostFileNameFormatChanged(string value) => UpdatePostPreview();
partial void OnPaidMessageFileNameFormatChanged(string value) => UpdatePaidMessagePreview();
partial void OnMessageFileNameFormatChanged(string value) => UpdateMessagePreview();
private bool Validate()
{
UsernameError = string.Empty;
if (string.IsNullOrWhiteSpace(Username))
{
UsernameError = "Username is required.";
return false;
}
string trimmedUsername = Username.Trim();
if (!IsEditMode || trimmedUsername != OriginalUsername)
{
if (_isUsernameDuplicate())
{
UsernameError = "A config for this username already exists.";
return false;
}
}
return true;
}
private void ClearAllPreviews()
{
PaidPostSegments.Clear();
PostSegments.Clear();
PaidMessageSegments.Clear();
MessageSegments.Clear();
UnknownPaidPostVariablesMessage = string.Empty;
UnknownPostVariablesMessage = string.Empty;
UnknownPaidMessageVariablesMessage = string.Empty;
UnknownMessageVariablesMessage = string.Empty;
}
private void UpdateAllPreviews()
{
UpdatePaidPostPreview();
UpdatePostPreview();
UpdatePaidMessagePreview();
UpdateMessagePreview();
}
private void UpdatePaidPostPreview() =>
UpdateFileNamePreview(PaidPostFileNameFormat, PaidPostSegments, s_postFileNameVariables, msg => UnknownPaidPostVariablesMessage = msg);
private void UpdatePostPreview() =>
UpdateFileNamePreview(PostFileNameFormat, PostSegments, s_postFileNameVariables, msg => UnknownPostVariablesMessage = msg);
private void UpdatePaidMessagePreview() =>
UpdateFileNamePreview(PaidMessageFileNameFormat, PaidMessageSegments, s_messageFileNameVariables, msg => UnknownPaidMessageVariablesMessage = msg);
private void UpdateMessagePreview() =>
UpdateFileNamePreview(MessageFileNameFormat, MessageSegments, s_messageFileNameVariables, msg => UnknownMessageVariablesMessage = msg);
private void UpdateFileNamePreview(string format, ObservableCollection<FileNameFormatSegmentViewModel> segments, string[] allowedVariables, Action<string> setUnknownMessage)
{
segments.Clear();
setUnknownMessage(string.Empty);
if (string.IsNullOrEmpty(format))
{
return;
}
HashSet<string> allowedSet = new(allowedVariables, StringComparer.OrdinalIgnoreCase);
HashSet<string> unknownVariables = new(StringComparer.OrdinalIgnoreCase);
(string PlainTextColor, string AllowedVariableColor, string InvalidVariableColor) = GetFileNamePreviewColors();
MatchCollection matches = s_fileNameVariableRegex.Matches(format);
int currentIndex = 0;
foreach (Match match in matches)
{
if (match.Index > currentIndex)
{
string plainText = format[currentIndex..match.Index];
segments.Add(new FileNameFormatSegmentViewModel(plainText, PlainTextColor));
}
string variableName = match.Groups[1].Value;
bool isAllowed = allowedSet.Contains(variableName);
segments.Add(new FileNameFormatSegmentViewModel(match.Value,
isAllowed ? AllowedVariableColor : InvalidVariableColor));
if (!isAllowed)
{
unknownVariables.Add(variableName);
}
currentIndex = match.Index + match.Length;
}
if (currentIndex < format.Length)
{
string trailingText = format[currentIndex..];
segments.Add(new FileNameFormatSegmentViewModel(trailingText, PlainTextColor));
}
if (unknownVariables.Count > 0)
{
string tokens = string.Join(", ", unknownVariables.Select(v => $"{{{v}}}"));
setUnknownMessage($"Unknown variable(s): {tokens}");
}
}
private static (string PlainTextColor, string AllowedVariableColor, string InvalidVariableColor)
GetFileNamePreviewColors()
{
bool isDarkTheme = Application.Current?.RequestedThemeVariant == ThemeVariant.Dark;
return isDarkTheme
? ("#DCE6F7", "#66A6FF", "#FF8C8C")
: ("#1F2A44", "#2E6EEA", "#D84E4E");
}
}