forked from sim0n00ps/OF-DL
434 lines
16 KiB
C#
434 lines
16 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;
|
|
private bool _isNormalizingUsername;
|
|
|
|
[ObservableProperty] private bool _isOpen;
|
|
[ObservableProperty] private bool _isEditMode;
|
|
[ObservableProperty] private string _originalUsername = string.Empty;
|
|
[ObservableProperty] private string _username = string.Empty;
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasUsernameError))]
|
|
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]
|
|
[NotifyPropertyChangedFor(nameof(HasUnknownPaidPostVariables))]
|
|
private string _unknownPaidPostVariablesMessage = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasUnknownPostVariables))]
|
|
private string _unknownPostVariablesMessage = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasUnknownPaidMessageVariables))]
|
|
private string _unknownPaidMessageVariablesMessage = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasUnknownMessageVariables))]
|
|
private string _unknownMessageVariablesMessage = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasPaidPostFileNameFormatError))]
|
|
private string _paidPostFileNameFormatError = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasPostFileNameFormatError))]
|
|
private string _postFileNameFormatError = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasPaidMessageFileNameFormatError))]
|
|
private string _paidMessageFileNameFormatError = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
[NotifyPropertyChangedFor(nameof(HasMessageFileNameFormatError))]
|
|
private string _messageFileNameFormatError = 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 bool HasPaidPostFileNameFormatError => !string.IsNullOrWhiteSpace(PaidPostFileNameFormatError);
|
|
public bool HasPostFileNameFormatError => !string.IsNullOrWhiteSpace(PostFileNameFormatError);
|
|
public bool HasPaidMessageFileNameFormatError => !string.IsNullOrWhiteSpace(PaidMessageFileNameFormatError);
|
|
public bool HasMessageFileNameFormatError => !string.IsNullOrWhiteSpace(MessageFileNameFormatError);
|
|
|
|
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;
|
|
PaidPostFileNameFormat = string.Empty;
|
|
PostFileNameFormat = string.Empty;
|
|
PaidMessageFileNameFormat = string.Empty;
|
|
MessageFileNameFormat = string.Empty;
|
|
ClearValidationErrors();
|
|
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;
|
|
PaidPostFileNameFormat = config.PaidPostFileNameFormat ?? string.Empty;
|
|
PostFileNameFormat = config.PostFileNameFormat ?? string.Empty;
|
|
PaidMessageFileNameFormat = config.PaidMessageFileNameFormat ?? string.Empty;
|
|
MessageFileNameFormat = config.MessageFileNameFormat ?? string.Empty;
|
|
ClearValidationErrors();
|
|
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 OnUsernameChanged(string value)
|
|
{
|
|
if (_isNormalizingUsername)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string trimmed = value.Trim();
|
|
if (!string.Equals(value, trimmed, StringComparison.Ordinal))
|
|
{
|
|
_isNormalizingUsername = true;
|
|
Username = trimmed;
|
|
_isNormalizingUsername = false;
|
|
}
|
|
|
|
UsernameError = string.Empty;
|
|
}
|
|
|
|
partial void OnPaidPostFileNameFormatChanged(string value) =>
|
|
HandleFileNameFormatChanged(
|
|
() => PaidPostFileNameFormatError = string.Empty,
|
|
UpdatePaidPostPreview);
|
|
|
|
partial void OnPostFileNameFormatChanged(string value) =>
|
|
HandleFileNameFormatChanged(
|
|
() => PostFileNameFormatError = string.Empty,
|
|
UpdatePostPreview);
|
|
|
|
partial void OnPaidMessageFileNameFormatChanged(string value) =>
|
|
HandleFileNameFormatChanged(
|
|
() => PaidMessageFileNameFormatError = string.Empty,
|
|
UpdatePaidMessagePreview);
|
|
|
|
partial void OnMessageFileNameFormatChanged(string value) =>
|
|
HandleFileNameFormatChanged(
|
|
() => MessageFileNameFormatError = string.Empty,
|
|
UpdateMessagePreview);
|
|
|
|
private bool Validate()
|
|
{
|
|
ClearValidationErrors();
|
|
TrimInputValues();
|
|
|
|
bool isValid = true;
|
|
|
|
if (string.IsNullOrWhiteSpace(Username))
|
|
{
|
|
UsernameError = "Username is required.";
|
|
isValid = false;
|
|
}
|
|
|
|
string trimmedUsername = Username.Trim();
|
|
if (isValid && (!IsEditMode || trimmedUsername != OriginalUsername))
|
|
{
|
|
if (_isUsernameDuplicate())
|
|
{
|
|
UsernameError = "A config for this username already exists.";
|
|
isValid = false;
|
|
}
|
|
}
|
|
|
|
ValidateFileNameFormatUniqueness(PaidPostFileNameFormat, message => PaidPostFileNameFormatError = message,
|
|
ref isValid);
|
|
ValidateFileNameFormatUniqueness(PostFileNameFormat, message => PostFileNameFormatError = message,
|
|
ref isValid);
|
|
ValidateFileNameFormatUniqueness(PaidMessageFileNameFormat, message => PaidMessageFileNameFormatError = message,
|
|
ref isValid);
|
|
ValidateFileNameFormatUniqueness(MessageFileNameFormat, message => MessageFileNameFormatError = message,
|
|
ref isValid);
|
|
|
|
return isValid;
|
|
}
|
|
|
|
private void ClearValidationErrors()
|
|
{
|
|
UsernameError = string.Empty;
|
|
PaidPostFileNameFormatError = string.Empty;
|
|
PostFileNameFormatError = string.Empty;
|
|
PaidMessageFileNameFormatError = string.Empty;
|
|
MessageFileNameFormatError = string.Empty;
|
|
}
|
|
|
|
private void TrimInputValues()
|
|
{
|
|
Username = Username.Trim();
|
|
PaidPostFileNameFormat = PaidPostFileNameFormat.Trim();
|
|
PostFileNameFormat = PostFileNameFormat.Trim();
|
|
PaidMessageFileNameFormat = PaidMessageFileNameFormat.Trim();
|
|
MessageFileNameFormat = MessageFileNameFormat.Trim();
|
|
}
|
|
|
|
private static void ValidateFileNameFormatUniqueness(
|
|
string format,
|
|
Action<string> setError,
|
|
ref bool isValid)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(format))
|
|
{
|
|
setError(string.Empty);
|
|
return;
|
|
}
|
|
|
|
bool hasUniqueToken = format.Contains("{mediaId}", StringComparison.OrdinalIgnoreCase) ||
|
|
format.Contains("{filename}", StringComparison.OrdinalIgnoreCase);
|
|
if (hasUniqueToken)
|
|
{
|
|
setError(string.Empty);
|
|
return;
|
|
}
|
|
|
|
setError("Format must include {mediaId} or {filename} to avoid file collisions.");
|
|
isValid = false;
|
|
}
|
|
|
|
private static void HandleFileNameFormatChanged(
|
|
Action clearError,
|
|
Action updatePreview)
|
|
{
|
|
clearError();
|
|
updatePreview();
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|