forked from sim0n00ps/OF-DL
Config updates
This commit is contained in:
parent
5af26156c7
commit
7cccdd58a0
BIN
OF DL.Gui/Assets/icon.ico
Normal file
BIN
OF DL.Gui/Assets/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@ -7,11 +7,11 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
<ApplicationIcon>Icon\download.ico</ApplicationIcon>
|
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Icon\download.ico"/>
|
<AvaloniaResource Include="Assets\icon.ico"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -17,10 +17,32 @@ public sealed class ConfigCategoryViewModel : ViewModelBase
|
|||||||
CustomDateField = fieldList.FirstOrDefault(field =>
|
CustomDateField = fieldList.FirstOrDefault(field =>
|
||||||
string.Equals(field.PropertyName, nameof(Config.CustomDate), StringComparison.Ordinal));
|
string.Equals(field.PropertyName, nameof(Config.CustomDate), StringComparison.Ordinal));
|
||||||
|
|
||||||
|
LimitDownloadRateField = fieldList.FirstOrDefault(field =>
|
||||||
|
string.Equals(field.PropertyName, nameof(Config.LimitDownloadRate), StringComparison.Ordinal));
|
||||||
|
DownloadLimitInMbPerSecField = fieldList.FirstOrDefault(field =>
|
||||||
|
string.Equals(field.PropertyName, nameof(Config.DownloadLimitInMbPerSec), StringComparison.Ordinal));
|
||||||
|
|
||||||
|
FolderPerPaidPostField = fieldList.FirstOrDefault(field =>
|
||||||
|
string.Equals(field.PropertyName, nameof(Config.FolderPerPaidPost), StringComparison.Ordinal));
|
||||||
|
FolderPerPostField = fieldList.FirstOrDefault(field =>
|
||||||
|
string.Equals(field.PropertyName, nameof(Config.FolderPerPost), StringComparison.Ordinal));
|
||||||
|
FolderPerPaidMessageField = fieldList.FirstOrDefault(field =>
|
||||||
|
string.Equals(field.PropertyName, nameof(Config.FolderPerPaidMessage), StringComparison.Ordinal));
|
||||||
|
FolderPerMessageField = fieldList.FirstOrDefault(field =>
|
||||||
|
string.Equals(field.PropertyName, nameof(Config.FolderPerMessage), StringComparison.Ordinal));
|
||||||
|
|
||||||
IEnumerable<ConfigFieldViewModel> visibleFields = IsDownloadBehavior
|
IEnumerable<ConfigFieldViewModel> visibleFields = IsDownloadBehavior
|
||||||
? fieldList.Where(field => field.PropertyName is not nameof(Config.DownloadOnlySpecificDates)
|
? fieldList.Where(field => field.PropertyName is not nameof(Config.DownloadOnlySpecificDates)
|
||||||
and not nameof(Config.DownloadDateSelection)
|
and not nameof(Config.DownloadDateSelection)
|
||||||
and not nameof(Config.CustomDate))
|
and not nameof(Config.CustomDate))
|
||||||
|
: IsPerformance
|
||||||
|
? fieldList.Where(field => field.PropertyName is not nameof(Config.LimitDownloadRate)
|
||||||
|
and not nameof(Config.DownloadLimitInMbPerSec))
|
||||||
|
: IsFolderStructure
|
||||||
|
? fieldList.Where(field => field.PropertyName is not nameof(Config.FolderPerPaidPost)
|
||||||
|
and not nameof(Config.FolderPerPost)
|
||||||
|
and not nameof(Config.FolderPerPaidMessage)
|
||||||
|
and not nameof(Config.FolderPerMessage))
|
||||||
: fieldList;
|
: fieldList;
|
||||||
|
|
||||||
foreach (ConfigFieldViewModel field in visibleFields)
|
foreach (ConfigFieldViewModel field in visibleFields)
|
||||||
@ -34,17 +56,48 @@ public sealed class ConfigCategoryViewModel : ViewModelBase
|
|||||||
public bool IsDownloadBehavior =>
|
public bool IsDownloadBehavior =>
|
||||||
string.Equals(CategoryName, "Download Behavior", StringComparison.Ordinal);
|
string.Equals(CategoryName, "Download Behavior", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
public bool IsPerformance =>
|
||||||
|
string.Equals(CategoryName, "Performance", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
public bool IsFolderStructure =>
|
||||||
|
string.Equals(CategoryName, "Folder Structure", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
public bool IsFileNaming =>
|
||||||
|
string.Equals(CategoryName, "File Naming", StringComparison.Ordinal);
|
||||||
|
|
||||||
public ConfigFieldViewModel? DownloadOnlySpecificDatesField { get; }
|
public ConfigFieldViewModel? DownloadOnlySpecificDatesField { get; }
|
||||||
|
|
||||||
public ConfigFieldViewModel? DownloadDateSelectionField { get; }
|
public ConfigFieldViewModel? DownloadDateSelectionField { get; }
|
||||||
|
|
||||||
public ConfigFieldViewModel? CustomDateField { get; }
|
public ConfigFieldViewModel? CustomDateField { get; }
|
||||||
|
|
||||||
|
public ConfigFieldViewModel? LimitDownloadRateField { get; }
|
||||||
|
|
||||||
|
public ConfigFieldViewModel? DownloadLimitInMbPerSecField { get; }
|
||||||
|
|
||||||
|
public ConfigFieldViewModel? FolderPerPaidPostField { get; }
|
||||||
|
|
||||||
|
public ConfigFieldViewModel? FolderPerPostField { get; }
|
||||||
|
|
||||||
|
public ConfigFieldViewModel? FolderPerPaidMessageField { get; }
|
||||||
|
|
||||||
|
public ConfigFieldViewModel? FolderPerMessageField { get; }
|
||||||
|
|
||||||
public bool HasSpecificDateFilterFields =>
|
public bool HasSpecificDateFilterFields =>
|
||||||
DownloadOnlySpecificDatesField != null &&
|
DownloadOnlySpecificDatesField != null &&
|
||||||
DownloadDateSelectionField != null &&
|
DownloadDateSelectionField != null &&
|
||||||
CustomDateField != null;
|
CustomDateField != null;
|
||||||
|
|
||||||
|
public bool HasRateLimitFields =>
|
||||||
|
LimitDownloadRateField != null &&
|
||||||
|
DownloadLimitInMbPerSecField != null;
|
||||||
|
|
||||||
|
public bool HasFolderStructureFields =>
|
||||||
|
FolderPerPaidPostField != null &&
|
||||||
|
FolderPerPostField != null &&
|
||||||
|
FolderPerPaidMessageField != null &&
|
||||||
|
FolderPerMessageField != null;
|
||||||
|
|
||||||
public string SpecificDateFilterHelpText
|
public string SpecificDateFilterHelpText
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -69,7 +122,59 @@ public sealed class ConfigCategoryViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string RateLimitHelpText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<string> parts = [];
|
||||||
|
if (!string.IsNullOrWhiteSpace(LimitDownloadRateField?.HelpText))
|
||||||
|
{
|
||||||
|
parts.Add(LimitDownloadRateField.HelpText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(DownloadLimitInMbPerSecField?.HelpText))
|
||||||
|
{
|
||||||
|
parts.Add(DownloadLimitInMbPerSecField.HelpText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(" ", parts.Distinct(StringComparer.Ordinal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FolderStructureHelpText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<string> parts = [];
|
||||||
|
if (!string.IsNullOrWhiteSpace(FolderPerPaidPostField?.HelpText))
|
||||||
|
{
|
||||||
|
parts.Add(FolderPerPaidPostField.HelpText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(FolderPerPostField?.HelpText))
|
||||||
|
{
|
||||||
|
parts.Add(FolderPerPostField.HelpText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(FolderPerPaidMessageField?.HelpText))
|
||||||
|
{
|
||||||
|
parts.Add(FolderPerPaidMessageField.HelpText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(FolderPerMessageField?.HelpText))
|
||||||
|
{
|
||||||
|
parts.Add(FolderPerMessageField.HelpText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(" ", parts.Distinct(StringComparer.Ordinal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool HasSpecificDateFilterHelpText => !string.IsNullOrWhiteSpace(SpecificDateFilterHelpText);
|
public bool HasSpecificDateFilterHelpText => !string.IsNullOrWhiteSpace(SpecificDateFilterHelpText);
|
||||||
|
|
||||||
|
public bool HasRateLimitHelpText => !string.IsNullOrWhiteSpace(RateLimitHelpText);
|
||||||
|
|
||||||
|
public bool HasFolderStructureHelpText => !string.IsNullOrWhiteSpace(FolderStructureHelpText);
|
||||||
|
|
||||||
public ObservableCollection<ConfigFieldViewModel> Fields { get; } = [];
|
public ObservableCollection<ConfigFieldViewModel> Fields { get; } = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,14 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, string> s_enumDisplayNames =
|
||||||
|
new(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
["_240"] = "240p",
|
||||||
|
["_720"] = "720p",
|
||||||
|
["source"] = "Source Resolution"
|
||||||
|
};
|
||||||
|
|
||||||
public ConfigFieldViewModel(
|
public ConfigFieldViewModel(
|
||||||
PropertyInfo propertyInfo,
|
PropertyInfo propertyInfo,
|
||||||
object? initialValue,
|
object? initialValue,
|
||||||
@ -59,7 +67,10 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
foreach (string enumName in Enum.GetNames(PropertyType))
|
foreach (string enumName in Enum.GetNames(PropertyType))
|
||||||
{
|
{
|
||||||
EnumOptions.Add(enumName);
|
string displayName = s_enumDisplayNames.TryGetValue(enumName, out string? mappedName)
|
||||||
|
? mappedName
|
||||||
|
: enumName;
|
||||||
|
EnumOptions.Add(displayName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,16 +119,24 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
|||||||
|
|
||||||
public bool IsNumeric { get; }
|
public bool IsNumeric { get; }
|
||||||
|
|
||||||
|
public bool IsNumericAndNotTimeout => IsNumeric && !IsTimeoutField;
|
||||||
|
|
||||||
public bool IsTextInput { get; }
|
public bool IsTextInput { get; }
|
||||||
|
|
||||||
public bool IsMultiline { get; }
|
public bool IsMultiline { get; }
|
||||||
|
|
||||||
public bool IsFileNameFormatField => s_fileNameVariablesByConfigOption.ContainsKey(PropertyName);
|
public bool IsFileNameFormatField => s_fileNameVariablesByConfigOption.ContainsKey(PropertyName);
|
||||||
|
|
||||||
|
public bool IsCreatorConfigsField =>
|
||||||
|
string.Equals(PropertyName, nameof(Config.CreatorConfigs), StringComparison.Ordinal);
|
||||||
|
|
||||||
public bool IsIgnoredUsersListField =>
|
public bool IsIgnoredUsersListField =>
|
||||||
string.Equals(PropertyName, nameof(Config.IgnoredUsersListName), StringComparison.Ordinal);
|
string.Equals(PropertyName, nameof(Config.IgnoredUsersListName), StringComparison.Ordinal);
|
||||||
|
|
||||||
public bool IsRegularTextInput => IsTextInput && !IsIgnoredUsersListField;
|
public bool IsTimeoutField =>
|
||||||
|
string.Equals(PropertyName, nameof(Config.Timeout), StringComparison.Ordinal);
|
||||||
|
|
||||||
|
public bool IsRegularTextInput => IsTextInput && !IsIgnoredUsersListField && !IsCreatorConfigsField;
|
||||||
|
|
||||||
public bool HasHelpText => !string.IsNullOrWhiteSpace(HelpText);
|
public bool HasHelpText => !string.IsNullOrWhiteSpace(HelpText);
|
||||||
|
|
||||||
@ -182,7 +201,11 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Enum.TryParse(PropertyType, EnumValue, true, out object? enumResult))
|
string actualEnumValue = s_enumDisplayNames
|
||||||
|
.FirstOrDefault(kvp => string.Equals(kvp.Value, EnumValue, StringComparison.Ordinal))
|
||||||
|
.Key ?? EnumValue;
|
||||||
|
|
||||||
|
if (!Enum.TryParse(PropertyType, actualEnumValue, true, out object? enumResult))
|
||||||
{
|
{
|
||||||
error = $"{DisplayName} must be one of: {string.Join(", ", EnumOptions)}.";
|
error = $"{DisplayName} must be one of: {string.Join(", ", EnumOptions)}.";
|
||||||
return false;
|
return false;
|
||||||
@ -359,7 +382,18 @@ public partial class ConfigFieldViewModel : ViewModelBase
|
|||||||
|
|
||||||
if (IsEnum)
|
if (IsEnum)
|
||||||
{
|
{
|
||||||
EnumValue = initialValue?.ToString() ?? EnumOptions.FirstOrDefault();
|
string? enumName = initialValue?.ToString();
|
||||||
|
if (!string.IsNullOrEmpty(enumName))
|
||||||
|
{
|
||||||
|
string displayName = s_enumDisplayNames.TryGetValue(enumName, out string? mappedName)
|
||||||
|
? mappedName
|
||||||
|
: enumName;
|
||||||
|
EnumValue = displayName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnumValue = EnumOptions.FirstOrDefault();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
118
OF DL.Gui/ViewModels/CreatorConfigEditorViewModel.cs
Normal file
118
OF DL.Gui/ViewModels/CreatorConfigEditorViewModel.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using OF_DL.Models.Config;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace OF_DL.Gui.ViewModels;
|
||||||
|
|
||||||
|
public partial class CreatorConfigEditorViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private CreatorConfigRowViewModel? _editingRow;
|
||||||
|
|
||||||
|
public ObservableCollection<CreatorConfigRowViewModel> Rows { get; } = [];
|
||||||
|
public ObservableCollection<string> AvailableUsers { get; }
|
||||||
|
|
||||||
|
[ObservableProperty] private CreatorConfigModalViewModel _modalViewModel;
|
||||||
|
|
||||||
|
public CreatorConfigEditorViewModel(IEnumerable<string> availableUsers)
|
||||||
|
{
|
||||||
|
AvailableUsers = new ObservableCollection<string>(availableUsers);
|
||||||
|
ModalViewModel = new CreatorConfigModalViewModel(AvailableUsers, OnModalClose, IsUsernameDuplicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadFromConfig(Dictionary<string, CreatorConfig> configs)
|
||||||
|
{
|
||||||
|
Rows.Clear();
|
||||||
|
foreach (KeyValuePair<string, CreatorConfig> kvp in configs.OrderBy(c => c.Key, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Rows.Add(new CreatorConfigRowViewModel(kvp.Key, kvp.Value, OnDeleteRow, OnEditRow));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, CreatorConfig> ToDictionary()
|
||||||
|
{
|
||||||
|
Dictionary<string, CreatorConfig> result = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (CreatorConfigRowViewModel row in Rows)
|
||||||
|
{
|
||||||
|
result[row.Username] = row.Config;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateAvailableUsers(IEnumerable<string> users)
|
||||||
|
{
|
||||||
|
AvailableUsers.Clear();
|
||||||
|
foreach (string user in users.OrderBy(u => u, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
AvailableUsers.Add(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void AddCreator()
|
||||||
|
{
|
||||||
|
Log.Information("AddCreator command executed");
|
||||||
|
Log.Information("ModalViewModel is null: {IsNull}", ModalViewModel == null);
|
||||||
|
_editingRow = null;
|
||||||
|
if (ModalViewModel != null)
|
||||||
|
{
|
||||||
|
Log.Information("Calling ModalViewModel.OpenForAdd()");
|
||||||
|
ModalViewModel.OpenForAdd();
|
||||||
|
Log.Information("After OpenForAdd - Modal IsOpen: {IsOpen}", ModalViewModel.IsOpen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error("ModalViewModel is null, cannot open modal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeleteRow(CreatorConfigRowViewModel row)
|
||||||
|
{
|
||||||
|
Rows.Remove(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEditRow(CreatorConfigRowViewModel row)
|
||||||
|
{
|
||||||
|
_editingRow = row;
|
||||||
|
ModalViewModel.OpenForEdit(row.Username, row.Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsUsernameDuplicate()
|
||||||
|
{
|
||||||
|
string username = ModalViewModel.Username.Trim();
|
||||||
|
if (_editingRow != null && string.Equals(_editingRow.Username, username, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Rows.Any(r => string.Equals(r.Username, username, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnModalClose(bool confirmed)
|
||||||
|
{
|
||||||
|
if (!confirmed)
|
||||||
|
{
|
||||||
|
ModalViewModel.IsOpen = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(string Username, CreatorConfig Config)? result = ModalViewModel.GetResult();
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_editingRow != null)
|
||||||
|
{
|
||||||
|
_editingRow.Username = result.Value.Username;
|
||||||
|
_editingRow.Config = result.Value.Config;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Rows.Add(new CreatorConfigRowViewModel(result.Value.Username, result.Value.Config, OnDeleteRow, OnEditRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalViewModel.IsOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
292
OF DL.Gui/ViewModels/CreatorConfigModalViewModel.cs
Normal file
292
OF DL.Gui/ViewModels/CreatorConfigModalViewModel.cs
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
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);
|
||||||
|
|
||||||
|
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, "#1F2A44"));
|
||||||
|
}
|
||||||
|
|
||||||
|
string variableName = match.Groups[1].Value;
|
||||||
|
bool isAllowed = allowedSet.Contains(variableName);
|
||||||
|
segments.Add(new FileNameFormatSegmentViewModel(match.Value, isAllowed ? "#2E6EEA" : "#D84E4E"));
|
||||||
|
|
||||||
|
if (!isAllowed)
|
||||||
|
{
|
||||||
|
unknownVariables.Add(variableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex = match.Index + match.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentIndex < format.Length)
|
||||||
|
{
|
||||||
|
string trailingText = format[currentIndex..];
|
||||||
|
segments.Add(new FileNameFormatSegmentViewModel(trailingText, "#1F2A44"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unknownVariables.Count > 0)
|
||||||
|
{
|
||||||
|
string tokens = string.Join(", ", unknownVariables.Select(v => $"{{{v}}}"));
|
||||||
|
setUnknownMessage($"Unknown variable(s): {tokens}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
OF DL.Gui/ViewModels/CreatorConfigRowViewModel.cs
Normal file
28
OF DL.Gui/ViewModels/CreatorConfigRowViewModel.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using OF_DL.Models.Config;
|
||||||
|
|
||||||
|
namespace OF_DL.Gui.ViewModels;
|
||||||
|
|
||||||
|
public partial class CreatorConfigRowViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly Action<CreatorConfigRowViewModel> _onDelete;
|
||||||
|
private readonly Action<CreatorConfigRowViewModel> _onEdit;
|
||||||
|
|
||||||
|
[ObservableProperty] private string _username;
|
||||||
|
[ObservableProperty] private CreatorConfig _config;
|
||||||
|
|
||||||
|
public CreatorConfigRowViewModel(string username, CreatorConfig config, Action<CreatorConfigRowViewModel> onDelete, Action<CreatorConfigRowViewModel> onEdit)
|
||||||
|
{
|
||||||
|
_username = username;
|
||||||
|
_config = config;
|
||||||
|
_onDelete = onDelete;
|
||||||
|
_onEdit = onEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void Delete() => _onDelete(this);
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void Edit() => _onEdit(this);
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ using OF_DL.Models;
|
|||||||
using OF_DL.Models.Config;
|
using OF_DL.Models.Config;
|
||||||
using OF_DL.Models.Downloads;
|
using OF_DL.Models.Downloads;
|
||||||
using OF_DL.Services;
|
using OF_DL.Services;
|
||||||
|
using Serilog;
|
||||||
using UserEntities = OF_DL.Models.Entities.Users;
|
using UserEntities = OF_DL.Models.Entities.Users;
|
||||||
|
|
||||||
namespace OF_DL.Gui.ViewModels;
|
namespace OF_DL.Gui.ViewModels;
|
||||||
@ -148,6 +149,8 @@ public partial class MainWindowViewModel(
|
|||||||
|
|
||||||
public ObservableCollection<string> ActivityLog { get; } = [];
|
public ObservableCollection<string> ActivityLog { get; } = [];
|
||||||
|
|
||||||
|
[ObservableProperty] private CreatorConfigEditorViewModel _creatorConfigEditor = new(Array.Empty<string>());
|
||||||
|
|
||||||
[ObservableProperty] private AppScreen _currentScreen = AppScreen.Loading;
|
[ObservableProperty] private AppScreen _currentScreen = AppScreen.Loading;
|
||||||
|
|
||||||
[ObservableProperty] private string _statusMessage = "Initializing...";
|
[ObservableProperty] private string _statusMessage = "Initializing...";
|
||||||
@ -162,22 +165,18 @@ public partial class MainWindowViewModel(
|
|||||||
|
|
||||||
[ObservableProperty] private string _ffmpegPath = string.Empty;
|
[ObservableProperty] private string _ffmpegPath = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasFfmpegPathError))]
|
||||||
[NotifyPropertyChangedFor(nameof(HasFfmpegPathError))]
|
|
||||||
private string _ffmpegPathError = string.Empty;
|
private string _ffmpegPathError = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty] private string _downloadPath = string.Empty;
|
[ObservableProperty] private string _downloadPath = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasDownloadPathError))]
|
||||||
[NotifyPropertyChangedFor(nameof(HasDownloadPathError))]
|
|
||||||
private string _downloadPathError = string.Empty;
|
private string _downloadPathError = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasMediaTypesError))]
|
||||||
[NotifyPropertyChangedFor(nameof(HasMediaTypesError))]
|
|
||||||
private string _mediaTypesError = string.Empty;
|
private string _mediaTypesError = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasMediaSourcesError))]
|
||||||
[NotifyPropertyChangedFor(nameof(HasMediaSourcesError))]
|
|
||||||
private string _mediaSourcesError = string.Empty;
|
private string _mediaSourcesError = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty] private string _authenticatedUserDisplay = "Not authenticated.";
|
[ObservableProperty] private string _authenticatedUserDisplay = "Not authenticated.";
|
||||||
@ -225,6 +224,66 @@ public partial class MainWindowViewModel(
|
|||||||
public string SelectedUsersSummary =>
|
public string SelectedUsersSummary =>
|
||||||
$"{AvailableUsers.Count(user => user.IsSelected)} / {AvailableUsers.Count} selected";
|
$"{AvailableUsers.Count(user => user.IsSelected)} / {AvailableUsers.Count} selected";
|
||||||
|
|
||||||
|
public bool? AllUsersSelected
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (AvailableUsers.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectedCount = AvailableUsers.Count(user => user.IsSelected);
|
||||||
|
if (selectedCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedCount == AvailableUsers.Count)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
bool? current = AllUsersSelected;
|
||||||
|
bool shouldSelectAll;
|
||||||
|
|
||||||
|
if (current == true)
|
||||||
|
{
|
||||||
|
shouldSelectAll = false;
|
||||||
|
}
|
||||||
|
else if (current == false)
|
||||||
|
{
|
||||||
|
shouldSelectAll = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shouldSelectAll = value == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdatingAllUsersSelected = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (SelectableUserViewModel user in AvailableUsers)
|
||||||
|
{
|
||||||
|
user.IsSelected = shouldSelectAll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isUpdatingAllUsersSelected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(SelectedUsersSummary));
|
||||||
|
DownloadSelectedCommand.NotifyCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isUpdatingAllUsersSelected;
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
public async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
if (HasInitialized)
|
if (HasInitialized)
|
||||||
@ -249,10 +308,7 @@ public partial class MainWindowViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task RetryStartupAsync()
|
private async Task RetryStartupAsync() => await BeginStartupAsync();
|
||||||
{
|
|
||||||
await BeginStartupAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void ExitApplication()
|
private void ExitApplication()
|
||||||
@ -331,10 +387,7 @@ public partial class MainWindowViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanRefreshUsers))]
|
[RelayCommand(CanExecute = nameof(CanRefreshUsers))]
|
||||||
private async Task RefreshUsersAsync()
|
private async Task RefreshUsersAsync() => await LoadUsersAndListsAsync();
|
||||||
{
|
|
||||||
await LoadUsersAndListsAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanRefreshIgnoredUsersLists))]
|
[RelayCommand(CanExecute = nameof(CanRefreshIgnoredUsersLists))]
|
||||||
private async Task RefreshIgnoredUsersListsAsync(ConfigFieldViewModel? field)
|
private async Task RefreshIgnoredUsersListsAsync(ConfigFieldViewModel? field)
|
||||||
@ -403,6 +456,27 @@ public partial class MainWindowViewModel(
|
|||||||
await EnsureAuthenticationAndLoadUsersAsync();
|
await EnsureAuthenticationAndLoadUsersAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void AddCreatorConfig()
|
||||||
|
{
|
||||||
|
Log.Information("=== AddCreatorConfig command called ===");
|
||||||
|
Log.Information("CreatorConfigEditor is null: {IsNull}", CreatorConfigEditor == null);
|
||||||
|
if (CreatorConfigEditor != null)
|
||||||
|
{
|
||||||
|
Log.Information("CreatorConfigEditor.AddCreatorCommand is null: {IsNull}", CreatorConfigEditor.AddCreatorCommand == null);
|
||||||
|
Log.Information("ModalViewModel is null: {IsNull}", CreatorConfigEditor.ModalViewModel == null);
|
||||||
|
if (CreatorConfigEditor.ModalViewModel != null)
|
||||||
|
{
|
||||||
|
Log.Information("ModalViewModel.IsOpen before: {IsOpen}", CreatorConfigEditor.ModalViewModel.IsOpen);
|
||||||
|
}
|
||||||
|
CreatorConfigEditor.AddCreatorCommand.Execute(null);
|
||||||
|
if (CreatorConfigEditor.ModalViewModel != null)
|
||||||
|
{
|
||||||
|
Log.Information("ModalViewModel.IsOpen after: {IsOpen}", CreatorConfigEditor.ModalViewModel.IsOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task StartBrowserLoginAsync()
|
private async Task StartBrowserLoginAsync()
|
||||||
{
|
{
|
||||||
@ -426,6 +500,7 @@ public partial class MainWindowViewModel(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await authService.SaveToFileAsync();
|
await authService.SaveToFileAsync();
|
||||||
bool isAuthValid = await ValidateCurrentAuthAsync();
|
bool isAuthValid = await ValidateCurrentAuthAsync();
|
||||||
if (!isAuthValid)
|
if (!isAuthValid)
|
||||||
@ -439,29 +514,6 @@ public partial class MainWindowViewModel(
|
|||||||
await LoadUsersAndListsAsync();
|
await LoadUsersAndListsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void SelectAllUsers()
|
|
||||||
{
|
|
||||||
foreach (SelectableUserViewModel user in AvailableUsers)
|
|
||||||
{
|
|
||||||
user.IsSelected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnPropertyChanged(nameof(SelectedUsersSummary));
|
|
||||||
AppendLog($"Selected all users ({AvailableUsers.Count}).");
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void SelectNoUsers()
|
|
||||||
{
|
|
||||||
foreach (SelectableUserViewModel user in AvailableUsers)
|
|
||||||
{
|
|
||||||
user.IsSelected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnPropertyChanged(nameof(SelectedUsersSummary));
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanApplySelectedList))]
|
[RelayCommand(CanExecute = nameof(CanApplySelectedList))]
|
||||||
private async Task SelectUsersFromListAsync()
|
private async Task SelectUsersFromListAsync()
|
||||||
{
|
{
|
||||||
@ -492,6 +544,7 @@ public partial class MainWindowViewModel(
|
|||||||
|
|
||||||
StatusMessage = $"Selected {selectedUsernames.Count} users from list '{SelectedListName}'.";
|
StatusMessage = $"Selected {selectedUsernames.Count} users from list '{SelectedListName}'.";
|
||||||
OnPropertyChanged(nameof(SelectedUsersSummary));
|
OnPropertyChanged(nameof(SelectedUsersSummary));
|
||||||
|
OnPropertyChanged(nameof(AllUsersSelected));
|
||||||
DownloadSelectedCommand.NotifyCanExecuteChanged();
|
DownloadSelectedCommand.NotifyCanExecuteChanged();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -502,16 +555,10 @@ public partial class MainWindowViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanDownloadSelected))]
|
[RelayCommand(CanExecute = nameof(CanDownloadSelected))]
|
||||||
private async Task DownloadSelectedAsync()
|
private async Task DownloadSelectedAsync() => await RunDownloadAsync(false);
|
||||||
{
|
|
||||||
await RunDownloadAsync(downloadPurchasedTabOnly: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanDownloadPurchasedTab))]
|
[RelayCommand(CanExecute = nameof(CanDownloadPurchasedTab))]
|
||||||
private async Task DownloadPurchasedTabAsync()
|
private async Task DownloadPurchasedTabAsync() => await RunDownloadAsync(true);
|
||||||
{
|
|
||||||
await RunDownloadAsync(downloadPurchasedTabOnly: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanStopWork))]
|
[RelayCommand(CanExecute = nameof(CanStopWork))]
|
||||||
private void StopWork()
|
private void StopWork()
|
||||||
@ -682,15 +729,9 @@ public partial class MainWindowViewModel(
|
|||||||
_ = SelectUsersFromListAsync();
|
_ = SelectUsersFromListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnFfmpegPathChanged(string value)
|
partial void OnFfmpegPathChanged(string value) => FfmpegPathError = string.Empty;
|
||||||
{
|
|
||||||
FfmpegPathError = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnDownloadPathChanged(string value)
|
partial void OnDownloadPathChanged(string value) => DownloadPathError = string.Empty;
|
||||||
{
|
|
||||||
DownloadPathError = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task BeginStartupAsync()
|
private async Task BeginStartupAsync()
|
||||||
{
|
{
|
||||||
@ -838,10 +879,13 @@ public partial class MainWindowViewModel(
|
|||||||
userViewModel.PropertyChanged += OnSelectableUserPropertyChanged;
|
userViewModel.PropertyChanged += OnSelectableUserPropertyChanged;
|
||||||
AvailableUsers.Add(userViewModel);
|
AvailableUsers.Add(userViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPropertyChanged(nameof(SelectedUsersSummary));
|
OnPropertyChanged(nameof(SelectedUsersSummary));
|
||||||
|
OnPropertyChanged(nameof(AllUsersSelected));
|
||||||
|
|
||||||
UpdateUserListsCollection();
|
UpdateUserListsCollection();
|
||||||
UpdateIgnoredUsersListFieldOptions();
|
UpdateIgnoredUsersListFieldOptions();
|
||||||
|
CreatorConfigEditor?.UpdateAvailableUsers(_allUsers.Keys);
|
||||||
|
|
||||||
SelectedListName = null;
|
SelectedListName = null;
|
||||||
|
|
||||||
@ -896,6 +940,7 @@ public partial class MainWindowViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ApplySpecialConfigValues(config);
|
ApplySpecialConfigValues(config);
|
||||||
|
config.CreatorConfigs = CreatorConfigEditor.ToDictionary();
|
||||||
ValidateSpecialConfigValues();
|
ValidateSpecialConfigValues();
|
||||||
if (HasSpecialConfigErrors())
|
if (HasSpecialConfigErrors())
|
||||||
{
|
{
|
||||||
@ -920,13 +965,6 @@ public partial class MainWindowViewModel(
|
|||||||
if (fieldMap.TryGetValue(error.Key, out ConfigFieldViewModel? field))
|
if (fieldMap.TryGetValue(error.Key, out ConfigFieldViewModel? field))
|
||||||
{
|
{
|
||||||
field.SetError(error.Value);
|
field.SetError(error.Value);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.Key.StartsWith($"{nameof(Config.CreatorConfigs)}.", StringComparison.Ordinal) &&
|
|
||||||
fieldMap.TryGetValue(nameof(Config.CreatorConfigs), out ConfigFieldViewModel? creatorConfigsField))
|
|
||||||
{
|
|
||||||
creatorConfigsField.SetError(error.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -941,6 +979,9 @@ public partial class MainWindowViewModel(
|
|||||||
ConfigCategoriesRight.Clear();
|
ConfigCategoriesRight.Clear();
|
||||||
BuildSpecialConfigInputs(config);
|
BuildSpecialConfigInputs(config);
|
||||||
|
|
||||||
|
CreatorConfigEditor = new CreatorConfigEditorViewModel(_allUsers.Keys);
|
||||||
|
CreatorConfigEditor.LoadFromConfig(config.CreatorConfigs);
|
||||||
|
|
||||||
IEnumerable<System.Reflection.PropertyInfo> properties = typeof(Config)
|
IEnumerable<System.Reflection.PropertyInfo> properties = typeof(Config)
|
||||||
.GetProperties()
|
.GetProperties()
|
||||||
.Where(property => property.CanRead && property.CanWrite)
|
.Where(property => property.CanRead && property.CanWrite)
|
||||||
@ -968,7 +1009,11 @@ public partial class MainWindowViewModel(
|
|||||||
int categoryIndex = 0;
|
int categoryIndex = 0;
|
||||||
foreach (IGrouping<string, ConfigFieldViewModel> group in grouped)
|
foreach (IGrouping<string, ConfigFieldViewModel> group in grouped)
|
||||||
{
|
{
|
||||||
ConfigCategoryViewModel category = new(group.Key, group.OrderBy(field => field.DisplayName));
|
IEnumerable<ConfigFieldViewModel> orderedFields = group.Key == "File Naming"
|
||||||
|
? group.OrderBy(field => GetFieldOrder(group.Key, field.PropertyName))
|
||||||
|
: group.OrderBy(field => field.DisplayName);
|
||||||
|
|
||||||
|
ConfigCategoryViewModel category = new(group.Key, orderedFields);
|
||||||
ConfigCategories.Add(category);
|
ConfigCategories.Add(category);
|
||||||
if (categoryIndex % 2 == 0)
|
if (categoryIndex % 2 == 0)
|
||||||
{
|
{
|
||||||
@ -983,6 +1028,25 @@ public partial class MainWindowViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int GetFieldOrder(string categoryName, string propertyName)
|
||||||
|
{
|
||||||
|
if (categoryName == "File Naming")
|
||||||
|
{
|
||||||
|
return propertyName switch
|
||||||
|
{
|
||||||
|
nameof(Config.RenameExistingFilesWhenCustomFormatIsSelected) => 0,
|
||||||
|
nameof(Config.PaidPostFileNameFormat) => 1,
|
||||||
|
nameof(Config.PostFileNameFormat) => 2,
|
||||||
|
nameof(Config.PaidMessageFileNameFormat) => 3,
|
||||||
|
nameof(Config.MessageFileNameFormat) => 4,
|
||||||
|
nameof(Config.CreatorConfigs) => 5,
|
||||||
|
_ => 100
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateIgnoredUsersListFieldOptions()
|
private void UpdateIgnoredUsersListFieldOptions()
|
||||||
{
|
{
|
||||||
IEnumerable<string> listNames = _allLists.Keys
|
IEnumerable<string> listNames = _allLists.Keys
|
||||||
@ -1213,12 +1277,16 @@ public partial class MainWindowViewModel(
|
|||||||
if (e.PropertyName == nameof(SelectableUserViewModel.IsSelected))
|
if (e.PropertyName == nameof(SelectableUserViewModel.IsSelected))
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(SelectedUsersSummary));
|
OnPropertyChanged(nameof(SelectedUsersSummary));
|
||||||
|
if (!_isUpdatingAllUsersSelected)
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(AllUsersSelected));
|
||||||
|
}
|
||||||
|
|
||||||
DownloadSelectedCommand.NotifyCanExecuteChanged();
|
DownloadSelectedCommand.NotifyCanExecuteChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartDownloadProgress(string description, long maxValue, bool showSize)
|
private void StartDownloadProgress(string description, long maxValue, bool showSize) =>
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
DownloadProgressDescription = description;
|
DownloadProgressDescription = description;
|
||||||
@ -1227,10 +1295,8 @@ public partial class MainWindowViewModel(
|
|||||||
IsDownloadProgressIndeterminate = maxValue <= 0;
|
IsDownloadProgressIndeterminate = maxValue <= 0;
|
||||||
IsDownloadProgressVisible = true;
|
IsDownloadProgressVisible = true;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private void IncrementDownloadProgress(long increment)
|
private void IncrementDownloadProgress(long increment) =>
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
if (IsDownloadProgressIndeterminate)
|
if (IsDownloadProgressIndeterminate)
|
||||||
@ -1240,10 +1306,8 @@ public partial class MainWindowViewModel(
|
|||||||
|
|
||||||
DownloadProgressValue = Math.Min(DownloadProgressMaximum, DownloadProgressValue + increment);
|
DownloadProgressValue = Math.Min(DownloadProgressMaximum, DownloadProgressValue + increment);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateProgressStatus(string message)
|
private void UpdateProgressStatus(string message) =>
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
if (IsDownloadProgressVisible)
|
if (IsDownloadProgressVisible)
|
||||||
@ -1251,10 +1315,8 @@ public partial class MainWindowViewModel(
|
|||||||
DownloadProgressDescription = message;
|
DownloadProgressDescription = message;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private void StopDownloadProgress()
|
private void StopDownloadProgress() =>
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
DownloadProgressDescription = string.Empty;
|
DownloadProgressDescription = string.Empty;
|
||||||
@ -1263,7 +1325,6 @@ public partial class MainWindowViewModel(
|
|||||||
IsDownloadProgressIndeterminate = false;
|
IsDownloadProgressIndeterminate = false;
|
||||||
IsDownloadProgressVisible = false;
|
IsDownloadProgressVisible = false;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private void ThrowIfStopRequested()
|
private void ThrowIfStopRequested()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,5 +8,13 @@ public partial class SelectableUserViewModel(string username, long userId) : Vie
|
|||||||
|
|
||||||
public long UserId { get; } = userId;
|
public long UserId { get; } = userId;
|
||||||
|
|
||||||
[ObservableProperty] private bool _isSelected;
|
public event EventHandler? SelectionChanged;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
partial void OnIsSelectedChanged(bool value)
|
||||||
|
{
|
||||||
|
SelectionChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
x:DataType="vm:MainWindowViewModel"
|
x:DataType="vm:MainWindowViewModel"
|
||||||
Width="1320"
|
Width="1320"
|
||||||
Height="860"
|
Height="860"
|
||||||
MinWidth="900"
|
MinWidth="1150"
|
||||||
MinHeight="700"
|
MinHeight="700"
|
||||||
Title="OF DL"
|
Title="OF DL"
|
||||||
Background="#EEF3FB"
|
Background="#EEF3FB"
|
||||||
@ -70,11 +70,6 @@
|
|||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Spacing="8"
|
Spacing="8"
|
||||||
HorizontalAlignment="Right">
|
HorizontalAlignment="Right">
|
||||||
<Button IsVisible="{Binding IsDownloading}"
|
|
||||||
Classes="primary"
|
|
||||||
Background="#D84E4E"
|
|
||||||
Command="{Binding StopWorkCommand}"
|
|
||||||
Content="Stop" />
|
|
||||||
<Button Classes="secondary"
|
<Button Classes="secondary"
|
||||||
IsVisible="{Binding IsAuthenticated}"
|
IsVisible="{Binding IsAuthenticated}"
|
||||||
Command="{Binding LogoutCommand}"
|
Command="{Binding LogoutCommand}"
|
||||||
@ -84,6 +79,12 @@
|
|||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Grid Grid.Row="2" Margin="14">
|
<Grid Grid.Row="2" Margin="14">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
<StackPanel IsVisible="{Binding IsLoadingScreen}"
|
<StackPanel IsVisible="{Binding IsLoadingScreen}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@ -232,6 +233,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Border Classes="surface" Padding="12">
|
<Border Classes="surface" Padding="12">
|
||||||
|
<StackPanel Spacing="14">
|
||||||
<Grid ColumnDefinitions="*,*">
|
<Grid ColumnDefinitions="*,*">
|
||||||
<ItemsControl x:Name="LeftConfigCategories"
|
<ItemsControl x:Name="LeftConfigCategories"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
@ -284,7 +286,8 @@
|
|||||||
Content="Browse..."
|
Content="Browse..."
|
||||||
Click="OnBrowseDownloadPathClick" />
|
Click="OnBrowseDownloadPathClick" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock IsVisible="{Binding DataContext.HasDownloadPathError, RelativeSource={RelativeSource AncestorType=Window}}"
|
<TextBlock
|
||||||
|
IsVisible="{Binding DataContext.HasDownloadPathError, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||||
Foreground="#FF5A5A"
|
Foreground="#FF5A5A"
|
||||||
Text="{Binding DataContext.DownloadPathError, RelativeSource={RelativeSource AncestorType=Window}}"
|
Text="{Binding DataContext.DownloadPathError, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
@ -319,8 +322,8 @@
|
|||||||
Content="?"
|
Content="?"
|
||||||
ToolTip.Tip="{Binding SpecificDateFilterHelpText}" />
|
ToolTip.Tip="{Binding SpecificDateFilterHelpText}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Grid ColumnDefinitions="Auto,Auto,*"
|
<Grid ColumnDefinitions="Auto,Auto,Auto"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Left">
|
||||||
<CheckBox Grid.Column="0"
|
<CheckBox Grid.Column="0"
|
||||||
Content="Enable"
|
Content="Enable"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@ -334,13 +337,109 @@
|
|||||||
ItemsSource="{Binding DownloadDateSelectionField.EnumOptions}"
|
ItemsSource="{Binding DownloadDateSelectionField.EnumOptions}"
|
||||||
SelectedItem="{Binding DownloadDateSelectionField.EnumValue}" />
|
SelectedItem="{Binding DownloadDateSelectionField.EnumValue}" />
|
||||||
<DatePicker Grid.Column="2"
|
<DatePicker Grid.Column="2"
|
||||||
MinWidth="320"
|
MinWidth="200"
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
IsEnabled="{Binding DownloadOnlySpecificDatesField.BoolValue}"
|
IsEnabled="{Binding DownloadOnlySpecificDatesField.BoolValue}"
|
||||||
SelectedDate="{Binding CustomDateField.DateValue}" />
|
SelectedDate="{Binding CustomDateField.DateValue}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<Grid IsVisible="{Binding HasRateLimitFields}"
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
ColumnDefinitions="190,*"
|
||||||
|
x:CompileBindings="False">
|
||||||
|
<Grid Grid.Column="0"
|
||||||
|
ColumnDefinitions="*,Auto"
|
||||||
|
Margin="0,6,10,0"
|
||||||
|
ClipToBounds="True">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#1F2A44"
|
||||||
|
Text="Limit Download Rate"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
VerticalAlignment="Top" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
IsVisible="{Binding HasRateLimitHelpText}"
|
||||||
|
Width="20"
|
||||||
|
Height="20"
|
||||||
|
MinWidth="20"
|
||||||
|
MinHeight="20"
|
||||||
|
Margin="6,0,0,0"
|
||||||
|
Padding="0"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
FontSize="12"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Background="#EAF0FB"
|
||||||
|
BorderBrush="#C5D4EC"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="10"
|
||||||
|
Content="?"
|
||||||
|
ToolTip.Tip="{Binding RateLimitHelpText}" />
|
||||||
|
</Grid>
|
||||||
|
<StackPanel Grid.Column="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="8"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<CheckBox Content="Enable"
|
||||||
|
IsChecked="{Binding LimitDownloadRateField.BoolValue}" />
|
||||||
|
<NumericUpDown MinWidth="120"
|
||||||
|
IsEnabled="{Binding LimitDownloadRateField.BoolValue}"
|
||||||
|
Value="{Binding DownloadLimitInMbPerSecField.NumericValue}"
|
||||||
|
Increment="1"
|
||||||
|
FormatString="N0" />
|
||||||
|
<TextBlock Text="Mbps"
|
||||||
|
Foreground="#4A5B78"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Grid IsVisible="{Binding HasFolderStructureFields}"
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
ColumnDefinitions="190,*"
|
||||||
|
x:CompileBindings="False">
|
||||||
|
<Grid Grid.Column="0"
|
||||||
|
ColumnDefinitions="*,Auto"
|
||||||
|
Margin="0,6,10,0"
|
||||||
|
ClipToBounds="True">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#1F2A44"
|
||||||
|
Text="Create Separate Folders For Each"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
VerticalAlignment="Top" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
IsVisible="{Binding HasFolderStructureHelpText}"
|
||||||
|
Width="20"
|
||||||
|
Height="20"
|
||||||
|
MinWidth="20"
|
||||||
|
MinHeight="20"
|
||||||
|
Margin="6,0,0,0"
|
||||||
|
Padding="0"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
FontSize="12"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Background="#EAF0FB"
|
||||||
|
BorderBrush="#C5D4EC"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="10"
|
||||||
|
Content="?"
|
||||||
|
ToolTip.Tip="{Binding FolderStructureHelpText}" />
|
||||||
|
</Grid>
|
||||||
|
<StackPanel Grid.Column="1" Spacing="6">
|
||||||
|
<CheckBox Content="Paid Posts"
|
||||||
|
IsChecked="{Binding FolderPerPaidPostField.BoolValue}" />
|
||||||
|
<CheckBox Content="Free Posts"
|
||||||
|
IsChecked="{Binding FolderPerPostField.BoolValue}" />
|
||||||
|
<CheckBox Content="Paid Messages"
|
||||||
|
IsChecked="{Binding FolderPerPaidMessageField.BoolValue}" />
|
||||||
|
<CheckBox Content="Free Messages"
|
||||||
|
IsChecked="{Binding FolderPerMessageField.BoolValue}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
<ItemsControl ItemsSource="{Binding Fields}">
|
<ItemsControl ItemsSource="{Binding Fields}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
@ -386,7 +485,8 @@
|
|||||||
IsChecked="{Binding BoolValue}" />
|
IsChecked="{Binding BoolValue}" />
|
||||||
|
|
||||||
<ComboBox IsVisible="{Binding IsEnum}"
|
<ComboBox IsVisible="{Binding IsEnum}"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Left"
|
||||||
|
MinWidth="160"
|
||||||
ItemsSource="{Binding EnumOptions}"
|
ItemsSource="{Binding EnumOptions}"
|
||||||
SelectedItem="{Binding EnumValue}" />
|
SelectedItem="{Binding EnumValue}" />
|
||||||
|
|
||||||
@ -394,12 +494,27 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
SelectedDate="{Binding DateValue}" />
|
SelectedDate="{Binding DateValue}" />
|
||||||
|
|
||||||
<NumericUpDown IsVisible="{Binding IsNumeric}"
|
<NumericUpDown
|
||||||
HorizontalAlignment="Stretch"
|
IsVisible="{Binding IsNumericAndNotTimeout}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
MinWidth="120"
|
||||||
Value="{Binding NumericValue}"
|
Value="{Binding NumericValue}"
|
||||||
Increment="1"
|
Increment="1"
|
||||||
FormatString="N0" />
|
FormatString="N0" />
|
||||||
|
|
||||||
|
<StackPanel IsVisible="{Binding IsTimeoutField}"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="8">
|
||||||
|
<NumericUpDown MinWidth="120"
|
||||||
|
Minimum="-1"
|
||||||
|
Value="{Binding NumericValue}"
|
||||||
|
Increment="1"
|
||||||
|
FormatString="N0" />
|
||||||
|
<TextBlock Text="seconds"
|
||||||
|
Foreground="#4A5B78"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<TextBox IsVisible="{Binding IsRegularTextInput}"
|
<TextBox IsVisible="{Binding IsRegularTextInput}"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
AcceptsReturn="{Binding IsMultiline}"
|
AcceptsReturn="{Binding IsMultiline}"
|
||||||
@ -415,8 +530,10 @@
|
|||||||
ItemsSource="{Binding IgnoredUsersListOptions}"
|
ItemsSource="{Binding IgnoredUsersListOptions}"
|
||||||
SelectedItem="{Binding SelectedIgnoredUsersListOption}">
|
SelectedItem="{Binding SelectedIgnoredUsersListOption}">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:ConfigSelectOptionViewModel">
|
<DataTemplate
|
||||||
<TextBlock Text="{Binding DisplayName}" />
|
x:DataType="vm:ConfigSelectOptionViewModel">
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding DisplayName}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
@ -441,7 +558,8 @@
|
|||||||
CommandParameter="{Binding}" />
|
CommandParameter="{Binding}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel IsVisible="{Binding IsFileNameFormatField}"
|
<StackPanel
|
||||||
|
IsVisible="{Binding IsFileNameFormatField}"
|
||||||
Spacing="6">
|
Spacing="6">
|
||||||
<Grid ColumnDefinitions="*,Auto">
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
<ComboBox Grid.Column="0"
|
<ComboBox Grid.Column="0"
|
||||||
@ -460,15 +578,19 @@
|
|||||||
BorderBrush="#D8E3F4"
|
BorderBrush="#D8E3F4"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="8">
|
CornerRadius="8">
|
||||||
<ItemsControl ItemsSource="{Binding FileNameFormatSegments}">
|
<ItemsControl
|
||||||
|
ItemsSource="{Binding FileNameFormatSegments}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<WrapPanel Orientation="Horizontal" />
|
<WrapPanel
|
||||||
|
Orientation="Horizontal" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
<DataTemplate
|
||||||
<TextBlock Text="{Binding Text}"
|
x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Text}"
|
||||||
Foreground="{Binding Foreground}"
|
Foreground="{Binding Foreground}"
|
||||||
FontFamily="Consolas"
|
FontFamily="Consolas"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
@ -477,13 +599,87 @@
|
|||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<TextBlock IsVisible="{Binding HasUnknownFileNameVariables}"
|
<TextBlock
|
||||||
|
IsVisible="{Binding HasUnknownFileNameVariables}"
|
||||||
Foreground="#FF5A5A"
|
Foreground="#FF5A5A"
|
||||||
Text="{Binding UnknownFileNameVariablesMessage}"
|
Text="{Binding UnknownFileNameVariablesMessage}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel IsVisible="{Binding IsCreatorConfigsField}"
|
||||||
|
Spacing="6"
|
||||||
|
x:CompileBindings="False">
|
||||||
|
<Button Classes="secondary"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Command="{Binding DataContext.AddCreatorConfigCommand, RelativeSource={RelativeSource AncestorType=Window}}">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<TextBlock Text="+" FontWeight="Bold" />
|
||||||
|
<TextBlock Text="Add Creator" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<ItemsControl ItemsSource="{Binding DataContext.CreatorConfigEditor.Rows, RelativeSource={RelativeSource AncestorType=Window}}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Spacing="6" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:CreatorConfigRowViewModel">
|
||||||
|
<Border Background="#F5F8FE"
|
||||||
|
BorderBrush="#D8E3F4"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="10">
|
||||||
|
<Grid ColumnDefinitions="*,Auto,Auto">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#1F2A44"
|
||||||
|
Text="{Binding Username}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Width="28"
|
||||||
|
Height="28"
|
||||||
|
MinWidth="28"
|
||||||
|
MinHeight="28"
|
||||||
|
Margin="0,0,6,0"
|
||||||
|
Padding="0"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
Background="#EAF0FB"
|
||||||
|
BorderBrush="#C5D4EC"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="6"
|
||||||
|
Content="✎"
|
||||||
|
ToolTip.Tip="Edit"
|
||||||
|
Command="{Binding EditCommand}" />
|
||||||
|
<Button Grid.Column="2"
|
||||||
|
Width="28"
|
||||||
|
Height="28"
|
||||||
|
MinWidth="28"
|
||||||
|
MinHeight="28"
|
||||||
|
Padding="0"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
Background="#FFE8E8"
|
||||||
|
BorderBrush="#E8C5C5"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="6"
|
||||||
|
Content="×"
|
||||||
|
ToolTip.Tip="Delete"
|
||||||
|
Command="{Binding DeleteCommand}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock IsVisible="{Binding HasError}"
|
<TextBlock IsVisible="{Binding HasError}"
|
||||||
Foreground="#FF5A5A"
|
Foreground="#FF5A5A"
|
||||||
Text="{Binding ErrorMessage}"
|
Text="{Binding ErrorMessage}"
|
||||||
@ -503,6 +699,7 @@
|
|||||||
ItemsSource="{Binding ConfigCategoriesRight}"
|
ItemsSource="{Binding ConfigCategoriesRight}"
|
||||||
ItemTemplate="{Binding ItemTemplate, ElementName=LeftConfigCategories}" />
|
ItemTemplate="{Binding ItemTemplate, ElementName=LeftConfigCategories}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
|
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
|
||||||
@ -542,20 +739,25 @@
|
|||||||
Padding="10"
|
Padding="10"
|
||||||
Margin="0,0,0,10"
|
Margin="0,0,0,10"
|
||||||
Classes="surface">
|
Classes="surface">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
<Button Content="Select All" Classes="secondary" Command="{Binding SelectAllUsersCommand}" />
|
<StackPanel Grid.Column="0"
|
||||||
<Button Content="Select None" Classes="secondary" Command="{Binding SelectNoUsersCommand}" />
|
Orientation="Horizontal"
|
||||||
<ComboBox Width="280"
|
Spacing="8"
|
||||||
PlaceholderText="Select a list of users"
|
VerticalAlignment="Center">
|
||||||
ItemsSource="{Binding UserLists}"
|
|
||||||
SelectedItem="{Binding SelectedListName}" />
|
|
||||||
<Button Content="Download Purchased Tab"
|
|
||||||
Classes="secondary"
|
|
||||||
Command="{Binding DownloadPurchasedTabCommand}" />
|
|
||||||
<Button Content="Download Selected"
|
<Button Content="Download Selected"
|
||||||
Classes="primary"
|
Classes="primary"
|
||||||
Command="{Binding DownloadSelectedCommand}" />
|
Command="{Binding DownloadSelectedCommand}" />
|
||||||
|
<Button Content="Download Purchased Tab"
|
||||||
|
Classes="secondary"
|
||||||
|
Command="{Binding DownloadPurchasedTabCommand}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
IsVisible="{Binding IsDownloading}"
|
||||||
|
Classes="primary"
|
||||||
|
Background="#D84E4E"
|
||||||
|
Command="{Binding StopWorkCommand}"
|
||||||
|
Content="Stop" />
|
||||||
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border Grid.Row="1"
|
<Border Grid.Row="1"
|
||||||
@ -564,11 +766,26 @@
|
|||||||
Padding="10"
|
Padding="10"
|
||||||
Classes="surface">
|
Classes="surface">
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<StackPanel Grid.Row="0" Spacing="3">
|
<Grid Grid.Row="0" ColumnDefinitions="*,Auto" VerticalAlignment="Center">
|
||||||
|
<StackPanel Grid.Column="0" Spacing="3">
|
||||||
<TextBlock FontWeight="SemiBold" Foreground="#1F2A44" Text="Users" />
|
<TextBlock FontWeight="SemiBold" Foreground="#1F2A44" Text="Users" />
|
||||||
<TextBlock Foreground="#4A5B78" Text="{Binding SelectedUsersSummary}" />
|
<CheckBox IsThreeState="True"
|
||||||
|
IsChecked="{Binding AllUsersSelected}"
|
||||||
|
Content="{Binding SelectedUsersSummary}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<ComboBox Grid.Column="1"
|
||||||
|
Width="280"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
PlaceholderText="Select a list of users"
|
||||||
|
ItemsSource="{Binding UserLists}"
|
||||||
|
SelectedItem="{Binding SelectedListName}" />
|
||||||
|
</Grid>
|
||||||
<ListBox Grid.Row="1" Margin="0,8,0,0" ItemsSource="{Binding AvailableUsers}">
|
<ListBox Grid.Row="1" Margin="0,8,0,0" ItemsSource="{Binding AvailableUsers}">
|
||||||
|
<ListBox.Styles>
|
||||||
|
<Style Selector="ListBoxItem">
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
</Style>
|
||||||
|
</ListBox.Styles>
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:SelectableUserViewModel">
|
<DataTemplate x:DataType="vm:SelectableUserViewModel">
|
||||||
<CheckBox Content="{Binding Username}" IsChecked="{Binding IsSelected}" />
|
<CheckBox Content="{Binding Username}" IsChecked="{Binding IsSelected}" />
|
||||||
@ -622,4 +839,227 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Modal Overlay -->
|
||||||
|
<Grid Grid.RowSpan="3"
|
||||||
|
IsVisible="{Binding CreatorConfigEditor.ModalViewModel.IsOpen}"
|
||||||
|
Background="#80000000"
|
||||||
|
ZIndex="1000">
|
||||||
|
<Border Background="#FFFFFF"
|
||||||
|
BorderBrush="#DDE5F3"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="12"
|
||||||
|
Padding="24"
|
||||||
|
Width="700"
|
||||||
|
MaxHeight="750"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
BoxShadow="0 8 32 0 #26000000">
|
||||||
|
<ScrollViewer DataContext="{Binding CreatorConfigEditor.ModalViewModel}">
|
||||||
|
<StackPanel Spacing="14" Margin="8,4,8,4" x:DataType="vm:CreatorConfigModalViewModel">
|
||||||
|
<TextBlock FontSize="18"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#1F2A44"
|
||||||
|
Text="{Binding DialogTitle}" />
|
||||||
|
|
||||||
|
<StackPanel Spacing="6">
|
||||||
|
<TextBlock FontWeight="SemiBold" Foreground="#1F2A44" Text="Username" />
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<AutoCompleteBox Grid.Column="0"
|
||||||
|
Text="{Binding Username}"
|
||||||
|
ItemsSource="{Binding AvailableUsers}"
|
||||||
|
Watermark="Enter or select username"
|
||||||
|
FilterMode="Contains" />
|
||||||
|
</Grid>
|
||||||
|
<TextBlock IsVisible="{Binding HasUsernameError}"
|
||||||
|
Foreground="#FF5A5A"
|
||||||
|
Text="{Binding UsernameError}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock FontWeight="SemiBold"
|
||||||
|
Foreground="#4A5B78"
|
||||||
|
Text="Filename Formats (leave blank to use global defaults)" />
|
||||||
|
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
<TextBlock FontWeight="SemiBold" Foreground="#1F2A44" Text="Paid Post Filename Format" />
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<ComboBox Grid.Column="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding PaidPostVariables}"
|
||||||
|
SelectedItem="{Binding SelectedPaidPostVariable}" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="secondary"
|
||||||
|
Content="Insert"
|
||||||
|
Command="{Binding InsertPaidPostVariableCommand}" />
|
||||||
|
</Grid>
|
||||||
|
<TextBox Text="{Binding PaidPostFileNameFormat}"
|
||||||
|
Watermark="Optional: override global paid post format" />
|
||||||
|
<Border Padding="8"
|
||||||
|
Background="#F5F8FE"
|
||||||
|
BorderBrush="#D8E3F4"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="8">
|
||||||
|
<ItemsControl ItemsSource="{Binding PaidPostSegments}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||||
|
<TextBlock Text="{Binding Text}"
|
||||||
|
Foreground="{Binding Foreground}"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Border>
|
||||||
|
<TextBlock IsVisible="{Binding HasUnknownPaidPostVariables}"
|
||||||
|
Foreground="#FF5A5A"
|
||||||
|
Text="{Binding UnknownPaidPostVariablesMessage}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
<TextBlock FontWeight="SemiBold" Foreground="#1F2A44" Text="Post Filename Format" />
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<ComboBox Grid.Column="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding PostVariables}"
|
||||||
|
SelectedItem="{Binding SelectedPostVariable}" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="secondary"
|
||||||
|
Content="Insert"
|
||||||
|
Command="{Binding InsertPostVariableCommand}" />
|
||||||
|
</Grid>
|
||||||
|
<TextBox Text="{Binding PostFileNameFormat}"
|
||||||
|
Watermark="Optional: override global post format" />
|
||||||
|
<Border Padding="8"
|
||||||
|
Background="#F5F8FE"
|
||||||
|
BorderBrush="#D8E3F4"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="8">
|
||||||
|
<ItemsControl ItemsSource="{Binding PostSegments}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||||
|
<TextBlock Text="{Binding Text}"
|
||||||
|
Foreground="{Binding Foreground}"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Border>
|
||||||
|
<TextBlock IsVisible="{Binding HasUnknownPostVariables}"
|
||||||
|
Foreground="#FF5A5A"
|
||||||
|
Text="{Binding UnknownPostVariablesMessage}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
<TextBlock FontWeight="SemiBold" Foreground="#1F2A44" Text="Paid Message Filename Format" />
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<ComboBox Grid.Column="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding PaidMessageVariables}"
|
||||||
|
SelectedItem="{Binding SelectedPaidMessageVariable}" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="secondary"
|
||||||
|
Content="Insert"
|
||||||
|
Command="{Binding InsertPaidMessageVariableCommand}" />
|
||||||
|
</Grid>
|
||||||
|
<TextBox Text="{Binding PaidMessageFileNameFormat}"
|
||||||
|
Watermark="Optional: override global paid message format" />
|
||||||
|
<Border Padding="8"
|
||||||
|
Background="#F5F8FE"
|
||||||
|
BorderBrush="#D8E3F4"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="8">
|
||||||
|
<ItemsControl ItemsSource="{Binding PaidMessageSegments}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||||
|
<TextBlock Text="{Binding Text}"
|
||||||
|
Foreground="{Binding Foreground}"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Border>
|
||||||
|
<TextBlock IsVisible="{Binding HasUnknownPaidMessageVariables}"
|
||||||
|
Foreground="#FF5A5A"
|
||||||
|
Text="{Binding UnknownPaidMessageVariablesMessage}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
<TextBlock FontWeight="SemiBold" Foreground="#1F2A44" Text="Message Filename Format" />
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<ComboBox Grid.Column="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding MessageVariables}"
|
||||||
|
SelectedItem="{Binding SelectedMessageVariable}" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="secondary"
|
||||||
|
Content="Insert"
|
||||||
|
Command="{Binding InsertMessageVariableCommand}" />
|
||||||
|
</Grid>
|
||||||
|
<TextBox Text="{Binding MessageFileNameFormat}"
|
||||||
|
Watermark="Optional: override global message format" />
|
||||||
|
<Border Padding="8"
|
||||||
|
Background="#F5F8FE"
|
||||||
|
BorderBrush="#D8E3F4"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="8">
|
||||||
|
<ItemsControl ItemsSource="{Binding MessageSegments}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:FileNameFormatSegmentViewModel">
|
||||||
|
<TextBlock Text="{Binding Text}"
|
||||||
|
Foreground="{Binding Foreground}"
|
||||||
|
FontFamily="Consolas"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Border>
|
||||||
|
<TextBlock IsVisible="{Binding HasUnknownMessageVariables}"
|
||||||
|
Foreground="#FF5A5A"
|
||||||
|
Text="{Binding UnknownMessageVariablesMessage}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
|
||||||
|
<Button Content="Cancel"
|
||||||
|
Classes="secondary"
|
||||||
|
Command="{Binding CancelCommand}" />
|
||||||
|
<Button Content="Confirm"
|
||||||
|
Classes="primary"
|
||||||
|
Command="{Binding ConfirmCommand}" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Platform;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using OF_DL.Gui.ViewModels;
|
using OF_DL.Gui.ViewModels;
|
||||||
|
|
||||||
@ -12,6 +14,7 @@ public partial class MainWindow : Window
|
|||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
Icon = new WindowIcon(AssetLoader.Open(new Uri("avares://OF DL.Gui/Assets/icon.ico")));
|
||||||
Opened += OnOpened;
|
Opened += OnOpened;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user