mirror of
https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies.git
synced 2026-07-25 06:14:31 +00:00
Refactor and enhance UI, localization, and file name handling
- Refactored file handling: added backup directories, improved mafile naming strategy, and introduced renaming functionality. - Enhanced UI: updated `MainWindow`, `SettingsView`, and dialogs with new controls, commands, and improved layouts. - Introduced `TextFieldDialog` for user input with localization support. - Improved localization: added new strings for buttons, errors, and tooltips. Removed all user-observed not localized strings - Fixed clipboard handling logic in `ClipboardHelper`. - Added 'Create Group' button in mafile's context menu and corresponding dialog view - Implemented `RequiresAdminAccess` check before updating program and requesting previlegies
This commit is contained in:
@@ -13,7 +13,7 @@ public class CodeProgressBar : ProgressBar
|
||||
|
||||
public static readonly DependencyProperty MaxTimeProperty =
|
||||
DependencyProperty.Register(nameof(MaxTime), typeof(double), typeof(CodeProgressBar),
|
||||
new PropertyMetadata(30.0)); // По умолчанию 30 сек
|
||||
new PropertyMetadata(30.0));
|
||||
|
||||
public double TimeRemaining
|
||||
{
|
||||
|
||||
@@ -26,15 +26,12 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Иконка типа (info/error) -->
|
||||
<materialDesign:PackIcon Height="22" Width="22"
|
||||
VerticalAlignment="Center"
|
||||
Margin="5,0,0,0"
|
||||
Foreground="{Binding IconBrush, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
Kind="{Binding IconKind, RelativeSource={RelativeSource AncestorType=UserControl}}" />
|
||||
|
||||
<!-- Текст подсказки -->
|
||||
<TextBlock Grid.Column="1"
|
||||
FontSize="{Binding FontSize, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
TextWrapping="WrapWithOverflow"
|
||||
@@ -42,12 +39,10 @@
|
||||
Margin="10,10,10,10"
|
||||
Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}" />
|
||||
|
||||
<!-- Крестик (кнопка закрытия) -->
|
||||
<Button Grid.Column="2"
|
||||
Visibility="{Binding ShowCloseButton, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Command="{Binding CloseCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
Style="{StaticResource MaterialDesignToolButton}"
|
||||
ToolTip="Закрыть"
|
||||
Margin="0,0,5,0"
|
||||
Padding="2">
|
||||
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace NebulaAuth.Converters;
|
||||
|
||||
public class BoolToMafileNamingConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not bool boolValue)
|
||||
return null;
|
||||
return boolValue ? "Login" : "Steam ID";
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
|
||||
<converters:AnyMafilesToVisibilityConverter x:Key="AnyMafilesToVisibilityConverter" />
|
||||
<converters:PortableMaClientStatusToColorConverter x:Key="PortableMaClientStatusToColorConverter" />
|
||||
<converters:BoolToMafileNamingConverter x:Key="BoolToMafileNamingConverter"/>
|
||||
|
||||
<converters:NullableToBooleanConverter x:Key="NullableToBooleanConverter" />
|
||||
<!-- Background converters-->
|
||||
|
||||
@@ -23,12 +23,12 @@ public static class DialogsController
|
||||
return result != null && (bool) result;
|
||||
}
|
||||
|
||||
//public static async Task<string?> ShowTextFieldDialog(string? msg = null)
|
||||
//{
|
||||
// var content = msg == null ? new TextFieldDialog() : new TextFieldDialog(msg);
|
||||
// var result = await DialogHost.Show(content);
|
||||
// return result as string;
|
||||
//}
|
||||
public static async Task<string?> ShowTextFieldDialog(string? title = null, string? msg = null)
|
||||
{
|
||||
var content = new TextFieldDialog(title, msg);
|
||||
var result = await DialogHost.Show(content);
|
||||
return result as string;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public static class TrayManager
|
||||
|
||||
var contextMenu = new ContextMenuStrip();
|
||||
|
||||
contextMenu.Items.Add("Выйти", null!, OnExitClick);
|
||||
contextMenu.Items.Add("Exit", null!, OnExitClick);
|
||||
|
||||
_notifyIcon.ContextMenuStrip = contextMenu;
|
||||
|
||||
|
||||
@@ -15,11 +15,25 @@ public static class UpdateManager
|
||||
var jsonPath = Path.Combine(Environment.CurrentDirectory, "update-settings.json");
|
||||
AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(jsonPath);
|
||||
AutoUpdater.ShowSkipButton = false;
|
||||
if (Settings.Instance.AllowAutoUpdate)
|
||||
AutoUpdater.UpdateMode = Mode.ForcedDownload;
|
||||
AutoUpdater.RunUpdateAsAdmin = RequiresAdminAccess();
|
||||
AutoUpdater.Start(UPDATE_URL);
|
||||
}
|
||||
|
||||
private static bool RequiresAdminAccess()
|
||||
{
|
||||
try
|
||||
{
|
||||
var testFile = Path.Combine(Environment.CurrentDirectory, "test.tmp");
|
||||
using (File.Create(testFile)) { }
|
||||
File.Delete(testFile);
|
||||
return false;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//static UpdateManager()
|
||||
//{
|
||||
|
||||
@@ -28,7 +28,14 @@
|
||||
<KeyBinding Command="{Binding Path=PasteMafilesFromClipboardCommand}"
|
||||
Key="V"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding Command="{x:Static ApplicationCommands.Find}"
|
||||
Key="F"
|
||||
Modifiers="Control" />
|
||||
</Window.InputBindings>
|
||||
<Window.CommandBindings>
|
||||
<CommandBinding Command="Find"
|
||||
Executed="FocusSearchBox"/>
|
||||
</Window.CommandBindings>
|
||||
<Border Name="DragNDropBorder" Panel.ZIndex="3" Opacity="1" AllowDrop="True"
|
||||
DragDrop.DragEnter="Rectangle_DragEnter" DragLeave="Rectangle_DragLeave" Drop="Rectangle_Drop">
|
||||
<md:DialogHost DialogOpened="DialogHost_DialogOpened"
|
||||
@@ -101,7 +108,9 @@
|
||||
<Separator />
|
||||
<ComboBox ToolTip="{Tr MainWindow.AppBar.GroupToolTip}"
|
||||
md:HintAssist.Hint="{Tr MainWindow.AppBar.GroupsHint}"
|
||||
MinWidth="100" Margin="8,0,8,0" VerticalAlignment="Center" IsEditable="True"
|
||||
MinWidth="100"
|
||||
MaxWidth="200"
|
||||
Margin="8,0,8,0" VerticalAlignment="Center" IsEditable="True"
|
||||
ItemsSource="{Binding Groups}"
|
||||
SelectedValue="{Binding SelectedGroup}"
|
||||
md:TextFieldAssist.HasClearButton="True">
|
||||
@@ -122,6 +131,7 @@
|
||||
<ComboBox ToolTip="{Tr MainWindow.AppBar.Proxy.ProxyManipulateToolTip}"
|
||||
|
||||
MinWidth="80"
|
||||
MaxWidth="200"
|
||||
Margin="8,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
md:HintAssist.Hint="{Tr MainWindow.AppBar.Proxy.ProxyHint}"
|
||||
@@ -256,6 +266,10 @@
|
||||
ItemsSource="{Binding MaFiles}"
|
||||
SelectedValue="{Binding SelectedMafile}"
|
||||
PreviewMouseDown="MafileListBox_OnPreviewMouseDown">
|
||||
<ListBox.Resources>
|
||||
<DiscreteObjectKeyFrame x:Key="groupsProxy" Value="{Binding Groups}"/>
|
||||
<DiscreteObjectKeyFrame x:Key="listBoxProxy" Value="{Binding ElementName=MafileListBox}"/>
|
||||
</ListBox.Resources>
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem"
|
||||
BasedOn="{StaticResource MaterialDesignListBoxItem}">
|
||||
@@ -301,48 +315,71 @@
|
||||
Command="{Binding DataContext.CopyMafileCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}" />
|
||||
</ListBox.InputBindings>
|
||||
<FrameworkElement.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem InputGestureText="Ctrl+C"
|
||||
Header="{Tr MainWindow.ContextMenus.Mafile.CopyLogin}"
|
||||
Command="{Binding CopyLoginCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopySteamId}"
|
||||
Command="{Binding CopySteamIdCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem InputGestureText="Ctrl+X"
|
||||
Header="{Tr MainWindow.ContextMenus.Mafile.CopyMafile}"
|
||||
Command="{Binding CopyMafileCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.AddToGroup}"
|
||||
ItemsSource="{Binding Groups}">
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource MaterialDesignMenuItem}"
|
||||
TargetType="{x:Type MenuItem}">
|
||||
<Setter Property="Command"
|
||||
Value="{Binding DataContext.AddToGroupCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<Setter Property="CommandParameter">
|
||||
<Setter.Value>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource MultiCommandParameterConverter}">
|
||||
<Binding />
|
||||
<Binding Path="PlacementTarget.SelectedValue"
|
||||
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
|
||||
</MultiBinding>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ItemsControl.ItemContainerStyle>
|
||||
</MenuItem>
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.RemoveFromGroup}"
|
||||
Command="{Binding Path=RemoveGroupCommand}"
|
||||
CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopyPassword}"
|
||||
Command="{Binding Path=CopyPasswordCommand}"
|
||||
CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
|
||||
<FrameworkElement.ContextMenu>
|
||||
<ContextMenu Tag="{Binding Groups}">
|
||||
<MenuItem InputGestureText="Ctrl+C"
|
||||
Header="{Tr MainWindow.ContextMenus.Mafile.CopyLogin}"
|
||||
Command="{Binding CopyLoginCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopySteamId}"
|
||||
Command="{Binding CopySteamIdCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem InputGestureText="Ctrl+X"
|
||||
Header="{Tr MainWindow.ContextMenus.Mafile.CopyMafile}"
|
||||
Command="{Binding CopyMafileCommand}"
|
||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.AddToGroup}"
|
||||
>
|
||||
<MenuItem.ItemsSource>
|
||||
<CompositeCollection>
|
||||
<MenuItem
|
||||
Command="{Binding CreateGroupCommand}"
|
||||
CommandParameter="{Binding Value.SelectedValue, Source={StaticResource listBoxProxy}}">
|
||||
<MenuItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<md:PackIcon Kind="PlusCircle" VerticalAlignment="Center"/>
|
||||
<TextBlock VerticalAlignment="Center" Margin="5,0,0,0" Grid.Column="1" Text="{Tr MainWindow.ContextMenus.Mafile.CreateGroup}"></TextBlock>
|
||||
</Grid>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<CollectionContainer Collection="{Binding Value, Source={StaticResource groupsProxy}}" />
|
||||
|
||||
</ContextMenu>
|
||||
</FrameworkElement.ContextMenu>
|
||||
</CompositeCollection>
|
||||
</MenuItem.ItemsSource>
|
||||
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource MaterialDesignMenuItem}"
|
||||
TargetType="{x:Type MenuItem}">
|
||||
<Setter Property="Command"
|
||||
Value="{Binding DataContext.AddToGroupCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
<Setter Property="CommandParameter">
|
||||
<Setter.Value>
|
||||
<MultiBinding
|
||||
Converter="{StaticResource MultiCommandParameterConverter}">
|
||||
<Binding />
|
||||
<Binding Path="PlacementTarget.SelectedValue"
|
||||
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
|
||||
</MultiBinding>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ItemsControl.ItemContainerStyle>
|
||||
</MenuItem>
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.RemoveFromGroup}"
|
||||
Command="{Binding Path=RemoveGroupCommand}"
|
||||
CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
|
||||
/>
|
||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopyPassword}"
|
||||
Command="{Binding Path=CopyPasswordCommand}"
|
||||
CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
|
||||
|
||||
</ContextMenu>
|
||||
</FrameworkElement.ContextMenu>
|
||||
|
||||
</ListBox>
|
||||
|
||||
@@ -368,7 +405,7 @@
|
||||
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.3" />
|
||||
</Border.BorderBrush>
|
||||
</Border>
|
||||
<md:Card Margin="10,10,15,10" Padding="2" UniformCornerRadius="12"
|
||||
<md:Card Margin="10,10,15,10" Padding="2" UniformCornerRadius="12"
|
||||
Background="Transparent" >
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -404,10 +441,13 @@
|
||||
<Button IsEnabled="{Binding IsMafileSelected}"
|
||||
FontSize="14"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}" Margin="0,10,0,10"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Margin="0,10,0,10"
|
||||
HorizontalAlignment="Center"
|
||||
Content="{Tr MainWindow.RightPart.LoadConfirmations}"
|
||||
Command="{Binding GetConfirmationsCommand}"
|
||||
md:ButtonProgressAssist.IsIndeterminate="True"
|
||||
md:ButtonProgressAssist.IsIndicatorVisible="{Binding GetConfirmationsCommand.IsRunning}"
|
||||
Width="{Binding Path=ActualWidth, Converter={StaticResource CoefficientConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource AncestorType=md:Card}}" />
|
||||
<ItemsControl md:RippleAssist.IsDisabled="True" Focusable="False" Grid.Row="2"
|
||||
FontSize="16"
|
||||
|
||||
@@ -121,4 +121,9 @@ public partial class MainWindow
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void FocusSearchBox(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
SearchField.Focus();
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ public partial class Mafile : MobileDataExtended
|
||||
}
|
||||
|
||||
[JsonIgnore] public string? Filename { get; set; }
|
||||
|
||||
[JsonIgnore] private PortableMaClient? _linkedClient;
|
||||
|
||||
public void SetSessionData(MobileSessionData? sessionData)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.Utility;
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace NebulaAuth.Model.Mafiles;
|
||||
|
||||
public interface IMafileNamingStrategy
|
||||
{
|
||||
bool CanNameMafile(Mafile mafile);
|
||||
string GetMafileName(Mafile mafile);
|
||||
}
|
||||
|
||||
public class MafileNamingStrategy : IMafileNamingStrategy
|
||||
{
|
||||
public static MafileNamingStrategy SteamId { get; } = new("{steamid}");
|
||||
public static MafileNamingStrategy Login { get; } = new("{login}");
|
||||
public const string DEF_EXTENSION = ".mafile";
|
||||
|
||||
public static readonly IDictionary<string, Func<Mafile, string?>> Placeholders =
|
||||
new Dictionary<string, Func<Mafile, string?>>
|
||||
{
|
||||
{"{steamid}", GetSteamId},
|
||||
{"{login}", GetLogin},
|
||||
{"{group}", GetGroup},
|
||||
{"{servertime}", GetServerTime}
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
|
||||
public string Pattern { get; }
|
||||
public bool IncludeExtension { get; }
|
||||
|
||||
public MafileNamingStrategy(string pattern, bool includeExtension = true)
|
||||
{
|
||||
Pattern = pattern;
|
||||
IncludeExtension = includeExtension;
|
||||
}
|
||||
|
||||
public bool CanNameMafile(Mafile mafile)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Pattern)) return false;
|
||||
var fileName = ApplyPatternSubstitution(mafile, Pattern, IncludeExtension);
|
||||
return FileNameValidator.IsValidFileName(fileName);
|
||||
}
|
||||
|
||||
public string GetMafileName(Mafile mafile)
|
||||
{
|
||||
if (!CanNameMafile(mafile)) throw new InvalidOperationException($"Cannot name mafile with the current pattern {Pattern}");
|
||||
return ApplyPatternSubstitution(mafile, Pattern, IncludeExtension);
|
||||
}
|
||||
|
||||
private static string ApplyPatternSubstitution(Mafile mafile, string pattern, bool includeExtension)
|
||||
{
|
||||
var result = pattern;
|
||||
foreach (var placeholder in Placeholders)
|
||||
{
|
||||
if (result.Contains(placeholder.Key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var value = placeholder.Value(mafile);
|
||||
result = result.Replace(placeholder.Key, value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
return includeExtension ? result + DEF_EXTENSION : result;
|
||||
}
|
||||
|
||||
private static string GetSteamId(Mafile mafile) => mafile.SteamId.Steam64.ToString();
|
||||
private static string GetLogin(Mafile mafile) => mafile.AccountName;
|
||||
private static string? GetGroup(Mafile mafile) => mafile.Group;
|
||||
private static string GetServerTime(Mafile mafile) => mafile.ServerTime.ToString();
|
||||
}
|
||||
@@ -46,7 +46,8 @@ public static class ProxyStorage
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SnackbarController.SendSnackbar("Ошибка при загрузке прокси");
|
||||
Shell.Logger.Error(ex, "Error while loading proxies");
|
||||
SnackbarController.SendSnackbar(LocManager.GetCodeBehindOrDefault("Error when loading proxies", "ProxyStorage", "ErrorWhileLoadingProxies"));
|
||||
SnackbarController.SendSnackbar(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public partial class Settings : ObservableObject
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SnackbarController.SendSnackbar("Ошибка при загрузке настроек. Настройки были сброшены");
|
||||
SnackbarController.SendSnackbar(LocManager.GetCodeBehindOrDefault("Error when loading settings", "Settings", "ErrorWhileLoadingSettings"));
|
||||
SnackbarController.SendSnackbar(ex.Message);
|
||||
Instance = new Settings();
|
||||
}
|
||||
|
||||
+129
-98
@@ -1,45 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AchiesUtilities.Extensions;
|
||||
using AchiesUtilities.Extensions;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.Model.Exceptions;
|
||||
using NebulaAuth.Model.Mafiles;
|
||||
using SteamLib;
|
||||
using SteamLib.Exceptions.Authorization;
|
||||
using SteamLib.SteamMobile;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
public static class Storage
|
||||
{
|
||||
public const string MAFILE_F = "maFiles";
|
||||
public const string REMOVED_F = "maFiles_removed";
|
||||
public static string MafileFolder { get; } = Path.GetFullPath(MAFILE_F);
|
||||
public static string RemovedMafileFolder { get; } = Path.GetFullPath(REMOVED_F);
|
||||
public const string DIR_MAFILES = "maFiles";
|
||||
public const string DIR_REMOVED_MAFILES = "maFiles_removed";
|
||||
public const string DIR_BACKUP_MAFILES = "maFiles_backup";
|
||||
public static string MafilesDirectory { get; } = Path.GetFullPath(DIR_MAFILES);
|
||||
public static string RemovedMafilesDirectory { get; } = Path.GetFullPath(DIR_REMOVED_MAFILES);
|
||||
public static string BackupMafilesDirectory { get; } = Path.GetFullPath(DIR_BACKUP_MAFILES);
|
||||
|
||||
public static ObservableCollection<Mafile> MaFiles { get; private set; } = new();
|
||||
|
||||
static Storage()
|
||||
{
|
||||
}
|
||||
public static ObservableCollection<Mafile> MaFiles { get; private set; } = [];
|
||||
|
||||
public static async Task Initialize(int threadCount, CancellationToken token = default)
|
||||
{
|
||||
if (!Directory.Exists(MafileFolder))
|
||||
Directory.CreateDirectory(MafileFolder);
|
||||
Directory.CreateDirectory(MafilesDirectory);
|
||||
Directory.CreateDirectory(RemovedMafilesDirectory);
|
||||
Directory.CreateDirectory(BackupMafilesDirectory);
|
||||
|
||||
if (!Directory.Exists(RemovedMafileFolder))
|
||||
Directory.CreateDirectory(RemovedMafileFolder);
|
||||
var comparer = new MafileNameComparer(Settings.Instance.UseAccountNameAsMafileName);
|
||||
var files = Directory
|
||||
.GetFiles(MafileFolder)
|
||||
.GetFiles(MafilesDirectory)
|
||||
.Where(file => Path.GetExtension(file).EqualsIgnoreCase(".mafile"))
|
||||
.Order(comparer)
|
||||
.ToList();
|
||||
|
||||
|
||||
@@ -47,7 +44,7 @@ public static class Storage
|
||||
await Task.Run(() =>
|
||||
{
|
||||
return Parallel.ForEachAsync(files,
|
||||
new ParallelOptions {CancellationToken = token, MaxDegreeOfParallelism = threadCount},
|
||||
new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = threadCount },
|
||||
async (file, ct) =>
|
||||
{
|
||||
try
|
||||
@@ -98,7 +95,7 @@ public static class Storage
|
||||
throw new FormatException("Can't generate code on this mafile", ex);
|
||||
}
|
||||
|
||||
if (overwrite == false && File.Exists(GetMafilePath(data)))
|
||||
if (overwrite == false && File.Exists(GetOrCreateMafilePath(data)))
|
||||
{
|
||||
throw new IOException("File already exist and overwrite is False"); //TODO: Custom Exception
|
||||
}
|
||||
@@ -106,13 +103,11 @@ public static class Storage
|
||||
|
||||
SaveMafile(data);
|
||||
}
|
||||
|
||||
public static Mafile ReadMafile(string path)
|
||||
{
|
||||
var str = File.ReadAllText(path);
|
||||
return NebulaSerializer.Deserialize(str, path);
|
||||
}
|
||||
|
||||
public static async Task<Mafile> ReadMafileAsync(string path)
|
||||
{
|
||||
var str = await File.ReadAllTextAsync(path);
|
||||
@@ -121,7 +116,7 @@ public static class Storage
|
||||
|
||||
public static void SaveMafile(Mafile data)
|
||||
{
|
||||
var path = GetMafilePath(data);
|
||||
var path = GetOrCreateMafilePath(data);
|
||||
var str = NebulaSerializer.SerializeMafile(data);
|
||||
File.WriteAllText(Path.GetFullPath(path), str);
|
||||
|
||||
@@ -136,18 +131,16 @@ public static class Storage
|
||||
MaFiles.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateMafile(Mafile data)
|
||||
{
|
||||
var path = GetMafilePath(data);
|
||||
var path = GetOrCreateMafilePath(data);
|
||||
var str = NebulaSerializer.SerializeMafile(data);
|
||||
File.WriteAllText(Path.GetFullPath(path), str);
|
||||
}
|
||||
|
||||
public static void MoveToRemoved(Mafile data)
|
||||
{
|
||||
var sourcePath = GetMafilePath(data);
|
||||
var destinationPath = Path.Combine(REMOVED_F, data.Filename + ".mafile");
|
||||
var sourcePath = GetOrCreateMafilePath(data);
|
||||
var destinationPath = Path.Combine(DIR_REMOVED_MAFILES, data.Filename + ".mafile");
|
||||
var destinationPathFinal = destinationPath;
|
||||
var i = 0;
|
||||
while (File.Exists(destinationPathFinal))
|
||||
@@ -160,98 +153,136 @@ public static class Storage
|
||||
File.Delete(sourcePath);
|
||||
MaFiles.Remove(data);
|
||||
}
|
||||
|
||||
private static string GetMafilePath(Mafile data)
|
||||
private static string GetOrCreateMafilePath(Mafile data)
|
||||
{
|
||||
if (data.Filename != null)
|
||||
{
|
||||
return Path.Combine(MafileFolder, data.Filename);
|
||||
return Path.Combine(MafilesDirectory, data.Filename);
|
||||
}
|
||||
|
||||
var fileName = Settings.Instance.UseAccountNameAsMafileName
|
||||
? CreateFileNameWithAccountName(data.AccountName)
|
||||
: CreateFileNameWithSteamId(data.SteamId);
|
||||
|
||||
var fileName = CreateMafileFileName(data, Settings.Instance.UseAccountNameAsMafileName);
|
||||
data.Filename = fileName;
|
||||
return Path.Combine(MafileFolder, fileName);
|
||||
return Path.Combine(MafilesDirectory, fileName);
|
||||
}
|
||||
|
||||
private static string CreateFileNameWithAccountName(string accountName)
|
||||
private static string CreateMafileFileName(Mafile data, bool useAccountName)
|
||||
{
|
||||
return accountName + ".mafile";
|
||||
}
|
||||
|
||||
private static string CreateFileNameWithSteamId(SteamId steamId)
|
||||
{
|
||||
return steamId.Steam64.Id + ".mafile";
|
||||
return useAccountName
|
||||
? MafileNamingStrategy.Login.GetMafileName(data)
|
||||
: MafileNamingStrategy.SteamId.GetMafileName(data);
|
||||
}
|
||||
|
||||
public static string? TryGetMafilePath(Mafile data)
|
||||
{
|
||||
if (data.Filename == null) return null;
|
||||
return Path.Combine(MafileFolder, data.Filename);
|
||||
return Path.Combine(MafilesDirectory, data.Filename);
|
||||
}
|
||||
|
||||
public static void BackupHandler(MobileDataExtended data)
|
||||
public static void WriteBackup(MobileDataExtended data)
|
||||
{
|
||||
if (Directory.Exists("mafiles_backup") == false)
|
||||
{
|
||||
Directory.CreateDirectory("mafiles_backup");
|
||||
}
|
||||
|
||||
|
||||
var json = NebulaSerializer.SerializeMafile(data, null);
|
||||
File.WriteAllText(Path.Combine("mafiles_backup", data.AccountName + ".mafile"),
|
||||
json);
|
||||
WriteBackup(data.AccountName, json);
|
||||
}
|
||||
public static void WriteBackup(string accountName, string data)
|
||||
{
|
||||
Directory.CreateDirectory(DIR_BACKUP_MAFILES);
|
||||
File.WriteAllText(Path.Combine(DIR_BACKUP_MAFILES, accountName + MafileNamingStrategy.DEF_EXTENSION), data);
|
||||
}
|
||||
|
||||
public static void BackupHandlerStr(string accountName, string data)
|
||||
public static async Task<MafileRenameResult> RenameMafiles(bool loginAsFileName, IProgress<double>? progress = null)
|
||||
{
|
||||
if (Directory.Exists("mafiles_backup") == false)
|
||||
if (MaFiles.Count == 0) return new MafileRenameResult();
|
||||
var now = DateTime.Now;
|
||||
var backupFileName = $"rename_backup_{now:yyyy-MM-dd_HH-mm-ss}.zip";
|
||||
var zipPath = Path.Combine(BackupMafilesDirectory, backupFileName);
|
||||
await using (var zipStream = new FileStream(zipPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
Directory.CreateDirectory("mafiles_backup");
|
||||
using var archive = new ZipArchive(zipStream, ZipArchiveMode.Create, leaveOpen: false);
|
||||
var files = Directory
|
||||
.EnumerateFiles(MafilesDirectory, "*.*", SearchOption.TopDirectoryOnly)
|
||||
.Where(f => Path.GetExtension(f).Equals(".mafile", StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
var counter = 0;
|
||||
foreach (var file in files)
|
||||
{
|
||||
counter++;
|
||||
var entry = archive.CreateEntry(Path.GetFileName(file), CompressionLevel.Optimal);
|
||||
|
||||
await using var entryStream = entry.Open();
|
||||
await using var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096,
|
||||
useAsync: true);
|
||||
await fileStream.CopyToAsync(entryStream);
|
||||
|
||||
if (counter % 5 == 0)
|
||||
{
|
||||
progress?.Report(counter / (double)files.Count);
|
||||
await Task.Delay(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File.WriteAllText(Path.Combine("mafiles_backup", accountName + ".mafile"),
|
||||
data);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Refactor
|
||||
//TODO: use numeric orderer when .net 10 released
|
||||
internal class MafileNameComparer : IComparer<string>
|
||||
{
|
||||
private const string MAF_64_START = "765";
|
||||
private static readonly IComparer<string> DefaultComparer = Comparer<string>.Default;
|
||||
public bool MafileNameMode { get; }
|
||||
|
||||
public MafileNameComparer(bool mafileNameMode)
|
||||
{
|
||||
MafileNameMode = mafileNameMode;
|
||||
}
|
||||
|
||||
|
||||
public int Compare(string? x, string? y)
|
||||
{
|
||||
if (x == null && y == null) return 0;
|
||||
if (x == null) return -1;
|
||||
if (y == null) return 1;
|
||||
|
||||
|
||||
var xisSteamId = Path.GetFileName(x).StartsWith(MAF_64_START);
|
||||
var yisSteamId = Path.GetFileName(y).StartsWith(MAF_64_START);
|
||||
|
||||
if (xisSteamId ^ yisSteamId)
|
||||
var mafiles = MaFiles.ToList();
|
||||
var res = new MafileRenameResult
|
||||
{
|
||||
if (MafileNameMode)
|
||||
Total = mafiles.Count,
|
||||
BackupFileName = backupFileName
|
||||
};
|
||||
|
||||
foreach (var mafile in mafiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
return xisSteamId ? 1 : -1;
|
||||
var targetFileName = CreateMafileFileName(mafile, loginAsFileName);
|
||||
if (mafile.Filename == targetFileName || mafile.Filename == null)
|
||||
{
|
||||
res.Renamed += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
var fullTargetPath = Path.Combine(MafilesDirectory, targetFileName);
|
||||
var fullSourcePath = Path.Combine(MafilesDirectory, mafile.Filename);
|
||||
if (!File.Exists(fullSourcePath))
|
||||
{
|
||||
Shell.Logger.Warn("Can't rename mafile {old} to {new} because source file not found",
|
||||
mafile.Filename, targetFileName);
|
||||
IncErrors();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (File.Exists(fullTargetPath))
|
||||
{
|
||||
Shell.Logger.Warn("Can't rename mafile {old} to {new} because target file already exist",
|
||||
mafile.Filename, targetFileName);
|
||||
res.AlreadyExist += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
File.Move(fullSourcePath, fullTargetPath);
|
||||
res.Renamed += 1;
|
||||
mafile.Filename = targetFileName;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Shell.Logger.Error(ex, "Error renaming mafile {file} {accountName}", mafile.Filename,
|
||||
mafile.AccountName);
|
||||
IncErrors();
|
||||
}
|
||||
|
||||
return yisSteamId ? 1 : -1;
|
||||
}
|
||||
return res;
|
||||
|
||||
return DefaultComparer.Compare(x, y);
|
||||
void IncErrors() => res.Errors += 1;
|
||||
}
|
||||
|
||||
public class MafileRenameResult
|
||||
{
|
||||
public int Total { get; set; }
|
||||
public int Renamed { get; set; }
|
||||
public int NotRenamed => Errors + AlreadyExist;
|
||||
public int Errors { get; set; }
|
||||
public int AlreadyExist { get; set; }
|
||||
public string BackupFileName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
namespace NebulaAuth.Theme.WindowStyle;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для WindowStyle.xaml
|
||||
/// </summary>
|
||||
partial class WindowStyle
|
||||
{
|
||||
public WindowStyle()
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace NebulaAuth.Utility;
|
||||
|
||||
@@ -10,24 +9,15 @@ public class ClipboardHelper
|
||||
{
|
||||
public static bool Set(string text)
|
||||
{
|
||||
var i = 0;
|
||||
while (i < 20)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetText(text);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (i == 19)
|
||||
{
|
||||
Shell.Logger.Error(ex);
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
System.Windows.Forms.Clipboard.SetText(text);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Shell.Logger.Error(ex);
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error"));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -35,26 +25,16 @@ public class ClipboardHelper
|
||||
|
||||
public static bool SetFiles(StringCollection files)
|
||||
{
|
||||
var i = 0;
|
||||
while (i < 20)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetFileDropList(files);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (i == 19)
|
||||
{
|
||||
Shell.Logger.Error(ex);
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
System.Windows.Forms.Clipboard.SetFileDropList(files);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Shell.Logger.Error(ex);
|
||||
SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Error", "Error"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace NebulaAuth.Utility;
|
||||
|
||||
public static class FileNameValidator
|
||||
{
|
||||
public static bool IsValidFileName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return false;
|
||||
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
return !name.Any(c => invalidChars.Contains(c));
|
||||
}
|
||||
|
||||
public static string GetInvalidChars(string name)
|
||||
{
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
return new string(name.Where(c => invalidChars.Contains(c)).Distinct().ToArray());
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
|
||||
mc:Ignorable="d"
|
||||
Height="Auto" Width="Auto" MaxWidth="500"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
@@ -40,27 +41,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
|
||||
Margin="10"
|
||||
Padding="5"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12">
|
||||
<Border.BorderBrush>
|
||||
<SolidColorBrush Color="{DynamicResource BaseShadowColor}" Opacity="0.5" />
|
||||
</Border.BorderBrush>
|
||||
<Border.Background>
|
||||
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.2" />
|
||||
</Border.Background>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="QuestionMarkCircleOutline" Foreground="{DynamicResource InfoBrush}"
|
||||
Height="22"
|
||||
Width="22" VerticalAlignment="Center" Margin="5,0,0,0" />
|
||||
<TextBlock x:Name="ConfirmTextBlock" VerticalAlignment="Center"
|
||||
MaxWidth="420" FontSize="14"
|
||||
TextWrapping="WrapWithOverflow"
|
||||
Margin="10,10,10,10" Text="Confirm?" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<nebulaAuth:HintBox FontSize="14" x:Name="ConfirmHint" Text="Confirm?" Margin="10"/>
|
||||
<Grid Grid.Row="1" Margin="10,0,10,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
@@ -73,18 +54,18 @@
|
||||
Margin="2,0,4,0"
|
||||
Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource True}">
|
||||
ОК
|
||||
</Button>
|
||||
CommandParameter="{StaticResource True}"
|
||||
Content="{Tr Common.OK}"/>
|
||||
<Button Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Width="130"
|
||||
Margin="0,0,2,0"
|
||||
IsCancel="True"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{StaticResource False}">
|
||||
Отмена
|
||||
</Button>
|
||||
CommandParameter="{StaticResource False}"
|
||||
Content="{Tr Common.Cancel}"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для ConfirmCancelDialog.xaml
|
||||
/// </summary>
|
||||
public partial class ConfirmCancelDialog
|
||||
{
|
||||
public ConfirmCancelDialog()
|
||||
@@ -13,6 +10,6 @@ public partial class ConfirmCancelDialog
|
||||
public ConfirmCancelDialog(string msg)
|
||||
{
|
||||
InitializeComponent();
|
||||
ConfirmTextBlock.Text = msg;
|
||||
ConfirmHint.Text = msg;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для LoginAgainDialog.xaml
|
||||
/// </summary>
|
||||
public partial class LoginAgainDialog
|
||||
{
|
||||
public LoginAgainDialog()
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для LoginAgainDialog.xaml
|
||||
/// </summary>
|
||||
public partial class LoginAgainOnImportDialog
|
||||
{
|
||||
public LoginAgainOnImportDialog()
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для SetCryptPasswordDialog.xaml
|
||||
/// </summary>
|
||||
|
||||
public partial class SetCryptPasswordDialog
|
||||
{
|
||||
public SetCryptPasswordDialog()
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<UserControl x:Class="NebulaAuth.View.Dialogs.TextFieldDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
Height="Auto" Width="Auto" MaxWidth="500"
|
||||
Background="{DynamicResource WindowBackground}">
|
||||
<Grid MinHeight="140" MinWidth="350">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="10,10,10,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!--<materialDesign:PackIcon Kind="WarningCircleOutline" Width="20" Height="20" Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryContentBrush}" />-->
|
||||
<TextBlock x:Name="TitleTextBlock" FontStyle="Normal" Foreground="{DynamicResource BaseContentBrush}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" FontSize="18" Text="{Tr TextFieldDialog.Title}" />
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
HorizontalAlignment="Right" CommandParameter="{x:Null}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
|
||||
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
|
||||
|
||||
</Button>
|
||||
</Grid>
|
||||
<Separator Grid.Row="1" Opacity="0.7" />
|
||||
<Grid Grid.Row="2" Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBox x:Name="TextField" Margin="10" materialDesign:HintAssist.Hint=""></TextBox>
|
||||
<Grid Grid.Row="1" Margin="10,0,10,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Width="130"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="2,0,4,0"
|
||||
Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
CommandParameter="{Binding Text, ElementName=TextField}"
|
||||
Content="{Tr Common.OK}"
|
||||
/>
|
||||
<Button Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Width="130"
|
||||
Margin="0,0,2,0"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
IsCancel="True"
|
||||
CommandParameter="{x:Null}"
|
||||
Content="{Tr Common.Cancel}"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,23 @@
|
||||
using MaterialDesignThemes.Wpf;
|
||||
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
|
||||
public partial class TextFieldDialog
|
||||
{
|
||||
public TextFieldDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public TextFieldDialog(string? title, string? msg)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(title))
|
||||
TitleTextBlock.Text = title;
|
||||
|
||||
if (!string.IsNullOrEmpty(msg))
|
||||
HintAssist.SetHint(TextField, msg);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,7 @@ using System.Windows;
|
||||
|
||||
namespace NebulaAuth.View.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для WaitLoginDialog.xaml
|
||||
/// </summary>
|
||||
|
||||
public partial class WaitLoginDialog
|
||||
{
|
||||
private TaskCompletionSource<string> _tcs = new();
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для LinkerView.xaml
|
||||
/// </summary>
|
||||
public partial class LinkerView
|
||||
{
|
||||
public LinkerView()
|
||||
|
||||
@@ -4,9 +4,7 @@ using System.Windows.Controls;
|
||||
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для LinksView.xaml
|
||||
/// </summary>
|
||||
|
||||
public partial class LinksView : UserControl
|
||||
{
|
||||
public LinksView()
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для MafileMoverView.xaml
|
||||
/// </summary>
|
||||
|
||||
public partial class MafileMoverView : UserControl
|
||||
{
|
||||
public MafileMoverView()
|
||||
|
||||
@@ -58,8 +58,18 @@
|
||||
<TextBlock HorizontalAlignment="Stretch">
|
||||
<Run Text="{Tr ProxyManagerDialog.DefaultProxy, IsDynamic=False}" />
|
||||
<Run Text="{Binding DefaultProxy.Key, StringFormat='
0:', FallbackValue='-', Mode=OneWay}" />
|
||||
<Run
|
||||
Text="{Binding DefaultProxy.Value, Converter='{StaticResource ProxyDataTextConverter}', Mode=OneWay, FallbackValue=''}" />
|
||||
<Run>
|
||||
<Run.Text>
|
||||
<MultiBinding Mode="OneWay"
|
||||
Converter="{StaticResource ProxyDataTextMultiConverter}">
|
||||
<Binding Path="DefaultProxy.Value" />
|
||||
<Binding Path="DataContext.DisplayProtocol"
|
||||
RelativeSource="{RelativeSource AncestorType=UserControl}" />
|
||||
<Binding Path="DataContext.DisplayCredentials"
|
||||
RelativeSource="{RelativeSource AncestorType=UserControl}" />
|
||||
</MultiBinding>
|
||||
</Run.Text>
|
||||
</Run>
|
||||
</TextBlock>
|
||||
|
||||
<Button Grid.Column="1" Command="{Binding RemoveDefaultCommand}">
|
||||
|
||||
@@ -3,9 +3,7 @@ using System.Windows.Input;
|
||||
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для ProxyManagerView.xaml
|
||||
/// </summary>
|
||||
|
||||
public partial class ProxyManagerView
|
||||
{
|
||||
public ProxyManagerView()
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
|
||||
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="650"
|
||||
FontFamily="{materialDesign:MaterialDesignFont}"
|
||||
MinHeight="400"
|
||||
MinWidth="400"
|
||||
MaxWidth="400"
|
||||
MinWidth="480"
|
||||
MaxWidth="480"
|
||||
d:DataContext="{d:DesignInstance other:SettingsVM}"
|
||||
Background="Transparent"
|
||||
FontSize="15">
|
||||
@@ -38,6 +39,7 @@
|
||||
<Button Grid.Column="1" Width="30" Height="30"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
IsEnabled="{Binding ApplyRenameSettingCommand.IsRunning, Converter={StaticResource ReverseBooleanConverter}, Mode=OneWay}"
|
||||
IsCancel="True">
|
||||
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
|
||||
</Button>
|
||||
@@ -47,15 +49,33 @@
|
||||
<TabControl materialDesign:ColorZoneAssist.Background="{DynamicResource Base100Brush}"
|
||||
Style="{StaticResource MaterialDesignUniformTabControl}" Grid.Row="2">
|
||||
<TabItem Header="{Tr SettingsDialog.MainSettings}">
|
||||
<StackPanel Width="340" Margin="10,20,10,40" Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<StackPanel Width="400" Margin="10,18,10,40" Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignFloatingHintComboBox}" Margin="0,15,0,0"
|
||||
|
||||
ItemsSource="{Binding Languages}" DisplayMemberPath="Value" SelectedValuePath="Key"
|
||||
SelectedValue="{Binding Language}"
|
||||
materialDesign:HintAssist.Hint="{Tr LanguageWord}" />
|
||||
<CheckBox Margin="0,15,0,0" IsChecked="{Binding HideToTray}"
|
||||
<CheckBox Margin="0,18,0,0" IsChecked="{Binding HideToTray}"
|
||||
Content="{Tr SettingsDialog.MinimizeToTray}" />
|
||||
<CheckBox Margin="0,15,0,0" IsChecked="{Binding UseIcon}"
|
||||
|
||||
|
||||
<CheckBox Margin="0,18,0,0" IsChecked="{Binding LegacyMode}"
|
||||
Content="{Tr SettingsDialog.LegacyMafileMode}"
|
||||
ToolTip="{Tr SettingsDialog.LegacyMafileModeHint}" />
|
||||
<!--<CheckBox IsEnabled="False" Margin="0,10,0,0" FontSize="16" IsChecked="{Binding AllowAutoUpdate}" Content="{Tr SettingsDialog.AllowAutoUpdate}"/>-->
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" BorderBrush="AliceBlue"
|
||||
Margin="0,18,0,0"
|
||||
BorderThickness="2"
|
||||
IsChecked="{Binding IgnorePatchTuesdayErrors}"
|
||||
ToolTip="{Tr SettingsDialog.IgnorePatchTuesdayErrorsHint}">
|
||||
<TextBlock TextWrapping="WrapWithOverflow"
|
||||
Text="{Tr SettingsDialog.IgnorePatchTuesdayErrors}" />
|
||||
</CheckBox>
|
||||
|
||||
<!--<CheckBox Margin="0,12,0,0" IsChecked="{Binding UseAccountNameAsMafileName}">
|
||||
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.UseAccountName}" />
|
||||
</CheckBox>-->
|
||||
<CheckBox Margin="0,20,0,0" IsChecked="{Binding UseIcon}"
|
||||
Content="{Tr SettingsDialog.UseIndicator}"
|
||||
ToolTip="{Tr SettingsDialog.UseIndicatorHint}" />
|
||||
<materialDesign:ColorPicker Margin="-5,0,-5,0" IsEnabled="{Binding UseIcon}"
|
||||
@@ -75,29 +95,36 @@
|
||||
<materialDesign:PackIcon Kind="ContentSave" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<CheckBox Margin="0,16,0,0" IsChecked="{Binding LegacyMode}"
|
||||
Content="{Tr SettingsDialog.LegacyMafileMode}"
|
||||
ToolTip="{Tr SettingsDialog.LegacyMafileModeHint}" />
|
||||
<!--<CheckBox IsEnabled="False" Margin="0,10,0,0" FontSize="16" IsChecked="{Binding AllowAutoUpdate}" Content="{Tr SettingsDialog.AllowAutoUpdate}"/>-->
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" BorderBrush="AliceBlue"
|
||||
Margin="0,12,0,0"
|
||||
BorderThickness="2"
|
||||
IsChecked="{Binding IgnorePatchTuesdayErrors}"
|
||||
ToolTip="{Tr SettingsDialog.IgnorePatchTuesdayErrorsHint}">
|
||||
<TextBlock TextWrapping="WrapWithOverflow"
|
||||
Text="{Tr SettingsDialog.IgnorePatchTuesdayErrors}" />
|
||||
</CheckBox>
|
||||
|
||||
<!--<CheckBox Margin="0,12,0,0" IsChecked="{Binding UseAccountNameAsMafileName}">
|
||||
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.UseAccountName}" />
|
||||
</CheckBox>-->
|
||||
<ComboBox materialDesign:HintAssist.Hint="Именование мафайлов"
|
||||
Margin="0,12,0,0"
|
||||
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
|
||||
<ComboBoxItem Content="steamId.mafile" />
|
||||
<ComboBoxItem Content="login.mafile" />
|
||||
<ComboBox materialDesign:HintAssist.Hint="{Tr SettingsDialog.MafileNamingMode}"
|
||||
Margin="0,25,0,0"
|
||||
Style="{StaticResource MaterialDesignFloatingHintComboBox}"
|
||||
SelectedItem="{Binding UseAccountNameAsMafileNamePreview}">
|
||||
<ComboBox.ItemsSource>
|
||||
<x:Array Type="{x:Type sys:Boolean}"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
<sys:Boolean>True</sys:Boolean>
|
||||
<sys:Boolean>False</sys:Boolean>
|
||||
</x:Array>
|
||||
|
||||
</ComboBox.ItemsSource>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource BoolToMafileNamingConverter}}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<Button>Применить и переименовать</Button>
|
||||
<Button Margin="0,10,0,0"
|
||||
materialDesign:ButtonProgressAssist.Maximum="1"
|
||||
materialDesign:ButtonProgressAssist.Value="{Binding RenameMafilesProgress}"
|
||||
materialDesign:ButtonProgressAssist.IsIndicatorVisible="{Binding ApplyRenameSettingCommand.IsRunning}"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Command="{Binding ApplyRenameSettingCommand}"
|
||||
Content="{Tr SettingsDialog.ApplyNamingMode}"
|
||||
/>
|
||||
<nebulaAuth:HintBox Margin="0,10,0,0" Text="{Binding RenameResultText}"
|
||||
FontSize="14"
|
||||
Visibility="{Binding RenameResultText, Converter={StaticResource NullableToVisibilityConverter}, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
|
||||
</TabItem>
|
||||
|
||||
@@ -5,9 +5,7 @@ using MaterialDesignThemes.Wpf;
|
||||
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для SettingsView.xaml
|
||||
/// </summary>
|
||||
|
||||
public partial class SettingsView
|
||||
{
|
||||
private readonly DialogHost? _dialogHost;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
namespace NebulaAuth.View;
|
||||
|
||||
/// <summary>
|
||||
/// Логика взаимодействия для UpdaterView.xaml
|
||||
/// </summary>
|
||||
public partial class UpdaterView
|
||||
{
|
||||
public UpdaterView()
|
||||
|
||||
@@ -177,7 +177,7 @@ public partial class LinkAccountVM : ObservableObject, ISmsCodeProvider, IPhoneN
|
||||
|
||||
|
||||
var linkOptions = new LinkOptions(_client, StaticLoginConsumer.Instance, this,
|
||||
this, this, Storage.BackupHandler, Logger2);
|
||||
this, this, Storage.WriteBackup, Logger2);
|
||||
var linker = new SteamAuthenticatorLinker(linkOptions);
|
||||
MobileDataExtended result;
|
||||
try
|
||||
@@ -226,7 +226,6 @@ public partial class LinkAccountVM : ObservableObject, ISmsCodeProvider, IPhoneN
|
||||
}
|
||||
|
||||
Storage.SaveMafile(mafile);
|
||||
File.Delete(Path.Combine("mafiles_backup", mafile.AccountName + ".mafile"));
|
||||
await Done(mafile.RevocationCode ?? string.Empty, mafile.SteamId.Steam64.ToString(), login);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
@@ -33,6 +24,14 @@ using SteamLibForked.Abstractions;
|
||||
using SteamLibForked.Exceptions.Authorization;
|
||||
using SteamLibForked.Models.Core;
|
||||
using SteamLibForked.Models.Session;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace NebulaAuth.ViewModel.MafileMover;
|
||||
@@ -236,7 +235,7 @@ public partial class MafileMoverVM : ObservableObject, IAuthProvider, IDisposabl
|
||||
|
||||
var t = res.ReplacementToken;
|
||||
var j = JsonConvert.SerializeObject(t);
|
||||
Storage.BackupHandlerStr(login, j);
|
||||
Storage.WriteBackup(login, j);
|
||||
var mobileData = t.ToMobileDataExtended(SteamAuthenticatorLinkerApi.GenerateDeviceId(), msd);
|
||||
|
||||
|
||||
@@ -254,7 +253,6 @@ public partial class MafileMoverVM : ObservableObject, IAuthProvider, IDisposabl
|
||||
}
|
||||
|
||||
Storage.SaveMafile(mafile);
|
||||
File.Delete(Path.Combine("mafiles_backup", mafile.AccountName + ".mafile"));
|
||||
await Done(mafile.RevocationCode ?? string.Empty, mafile.SteamId.Steam64.ToString());
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public partial class MainVM : ObservableObject
|
||||
[RelayCommand]
|
||||
public async Task Debug()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using NebulaAuth.Utility;
|
||||
using SteamLib.SteamMobile.Confirmations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
public partial class MainVM //Confirmations
|
||||
{
|
||||
public ObservableCollection<Confirmation> Confirmations { get; } = new();
|
||||
public ObservableCollection<Confirmation> Confirmations { get; } = [];
|
||||
public bool ConfirmationsVisible => SelectedMafile == _confirmationsLoadedForMafile;
|
||||
private Mafile? _confirmationsLoadedForMafile;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public partial class MainVM //File //TODO: Refactor
|
||||
{
|
||||
var mafile = SelectedMafile;
|
||||
|
||||
var path = Storage.MafileFolder;
|
||||
var path = Storage.MafilesDirectory;
|
||||
string? mafilePath = null;
|
||||
if (mafile != null)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AchiesUtilities.Extensions;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using NebulaAuth.Model.Entities;
|
||||
|
||||
@@ -33,9 +36,17 @@ public partial class MainVM //Groups
|
||||
[ObservableProperty] private ObservableCollection<string> _groups = [];
|
||||
|
||||
private string _searchText = string.Empty;
|
||||
|
||||
private string? _selectedGroup;
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CreateGroup(Mafile? mafile)
|
||||
{
|
||||
if(mafile == null) return;
|
||||
var res = await DialogsController.ShowTextFieldDialog(GetLocalization("CreateGroupTitle"), GetLocalization("CreateGroupInput"));
|
||||
if(string.IsNullOrWhiteSpace(res)) return;
|
||||
AddToGroup([res, mafile]);
|
||||
SnackbarController.SendSnackbar(GetLocalization("CreateGroupSuccess"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void AddGroup(string? value)
|
||||
@@ -68,7 +79,7 @@ public partial class MainVM //Groups
|
||||
OnPropertyChanged(nameof(SelectedMafile));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
[RelayCommand(CanExecute = nameof(RemoveGroupCanExecute))]
|
||||
private void RemoveGroup(Mafile? mafile)
|
||||
{
|
||||
if (mafile?.Group == null) return;
|
||||
@@ -85,6 +96,10 @@ public partial class MainVM //Groups
|
||||
PerformQuery();
|
||||
}
|
||||
|
||||
private bool RemoveGroupCanExecute(Mafile? mafile)
|
||||
{
|
||||
return mafile is {Group: not null};
|
||||
}
|
||||
|
||||
private void QueryGroups()
|
||||
{
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace NebulaAuth.ViewModel.Other;
|
||||
|
||||
@@ -11,6 +14,7 @@ public partial class SettingsVM : ObservableObject
|
||||
{
|
||||
public Settings Settings => Settings.Instance;
|
||||
|
||||
#region SettingsProps
|
||||
|
||||
public bool HideToTray
|
||||
{
|
||||
@@ -88,7 +92,11 @@ public partial class SettingsVM : ObservableObject
|
||||
public bool UseAccountNameAsMafileName
|
||||
{
|
||||
get => Settings.UseAccountNameAsMafileName;
|
||||
set => Settings.UseAccountNameAsMafileName = value;
|
||||
set
|
||||
{
|
||||
Settings.UseAccountNameAsMafileName = value;
|
||||
ApplyRenameSettingCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IgnorePatchTuesdayErrors
|
||||
@@ -97,28 +105,9 @@ public partial class SettingsVM : ObservableObject
|
||||
set => Settings.IgnorePatchTuesdayErrors = value;
|
||||
}
|
||||
|
||||
[ObservableProperty] private string? _password;
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private void SetPassword()
|
||||
{
|
||||
Settings.IsPasswordSet = PHandler.SetPassword(Password);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ResetThemeDefaults()
|
||||
{
|
||||
Settings.ResetThemeDefaults();
|
||||
OnPropertyChanged(nameof(BackgroundBlur));
|
||||
OnPropertyChanged(nameof(BackgroundOpacity));
|
||||
OnPropertyChanged(nameof(BackgroundGamma));
|
||||
OnPropertyChanged(nameof(LeftOpacity));
|
||||
OnPropertyChanged(nameof(RightOpacity));
|
||||
OnPropertyChanged(nameof(ApplyBlurBackground));
|
||||
OnPropertyChanged(nameof(RippleDisabled));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Theme
|
||||
|
||||
@@ -177,4 +166,90 @@ public partial class SettingsVM : ObservableObject
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[ObservableProperty] private string? _password;
|
||||
[ObservableProperty] private string? _renameResultText;
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(ApplyRenameSettingCommand))]
|
||||
private bool _useAccountNameAsMafileNamePreview;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _renameMafilesProgress;
|
||||
|
||||
public SettingsVM()
|
||||
{
|
||||
_useAccountNameAsMafileNamePreview = Settings.UseAccountNameAsMafileName;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SetPassword()
|
||||
{
|
||||
Settings.IsPasswordSet = PHandler.SetPassword(Password);
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ResetThemeDefaults()
|
||||
{
|
||||
Settings.ResetThemeDefaults();
|
||||
OnPropertyChanged(nameof(BackgroundBlur));
|
||||
OnPropertyChanged(nameof(BackgroundOpacity));
|
||||
OnPropertyChanged(nameof(BackgroundGamma));
|
||||
OnPropertyChanged(nameof(LeftOpacity));
|
||||
OnPropertyChanged(nameof(RightOpacity));
|
||||
OnPropertyChanged(nameof(ApplyBlurBackground));
|
||||
OnPropertyChanged(nameof(RippleDisabled));
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(ApplyRenameSettingCanExecute))]
|
||||
private async Task ApplyRenameSetting()
|
||||
{
|
||||
RenameResultText = null;
|
||||
var targetValue = UseAccountNameAsMafileNamePreview;
|
||||
if (UseAccountNameAsMafileName == targetValue) return;
|
||||
RenameMafilesProgress = 0;
|
||||
Storage.MafileRenameResult? result = null;
|
||||
try
|
||||
{
|
||||
result = await Storage.RenameMafiles(targetValue, new Progress<double>(p => RenameMafilesProgress = p));
|
||||
UseAccountNameAsMafileName = targetValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Shell.Logger.Error(ex, "Error while renaming mafiles");
|
||||
}
|
||||
finally
|
||||
{
|
||||
RenameMafilesProgress = 0;
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
RenameResultText = GetLoc("Error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.Total == 0) return;
|
||||
|
||||
if (result.NotRenamed == 0)
|
||||
{
|
||||
var l = GetLoc("AllRenamed");
|
||||
RenameResultText = string.Format(l, result.Total, result.BackupFileName);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var l = GetLoc("PartialSuccess");
|
||||
RenameResultText =
|
||||
string.Format(l, result.Total, result.Renamed, result.Errors, result.AlreadyExist, result.BackupFileName);
|
||||
|
||||
}
|
||||
|
||||
string GetLoc(string key)
|
||||
{
|
||||
return LocManager.GetCodeBehindOrDefault(key, "SettingsVM", "MafileRenaming", key);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ApplyRenameSettingCanExecute() => UseAccountNameAsMafileNamePreview != UseAccountNameAsMafileName;
|
||||
}
|
||||
@@ -45,6 +45,16 @@
|
||||
"ru": "Удалить",
|
||||
"ua": "Видалити"
|
||||
},
|
||||
"Cancel": {
|
||||
"en": "Cancel",
|
||||
"ru": "Отмена",
|
||||
"ua": "Скасувати"
|
||||
},
|
||||
"OK": {
|
||||
"en": "OK",
|
||||
"ru": "ОК",
|
||||
"ua": "ОК"
|
||||
},
|
||||
"Abbreviations": {
|
||||
"Time": {
|
||||
"Seconds": {
|
||||
@@ -290,6 +300,11 @@
|
||||
"ru": "Скопировать SteamID",
|
||||
"ua": "Скопіювати SteamID"
|
||||
},
|
||||
"CreateGroup": {
|
||||
"en": "Create group",
|
||||
"ru": "Создать группу",
|
||||
"ua": "Створити групу"
|
||||
},
|
||||
"AddToGroup": {
|
||||
"en": "Add to group",
|
||||
"ru": "Добавить в группу",
|
||||
@@ -432,7 +447,7 @@
|
||||
"ua": "Поточний пароль шифрування"
|
||||
},
|
||||
"Hint": {
|
||||
"en": "Doesn't saved on disk",
|
||||
"en": "Not saved to disk",
|
||||
"ru": "Не сохраняется в системе",
|
||||
"ua": "Не зберігається у системі"
|
||||
}
|
||||
@@ -453,10 +468,15 @@
|
||||
"ru": "Разрешить автообновление",
|
||||
"ua": "Дозволити автооновлення"
|
||||
},
|
||||
"UseAccountName": {
|
||||
"en": "Use account login on mafiles",
|
||||
"ru": "Использовать логин аккаунта на мафайлах",
|
||||
"ua": "Використовувати логін акаунта на мафайлах"
|
||||
"MafileNamingMode": {
|
||||
"en": "Mafile naming mode",
|
||||
"ru": "Режим именования мафайлов",
|
||||
"ua": "Режим іменування мафайлів"
|
||||
},
|
||||
"ApplyNamingMode": {
|
||||
"en": "Apply and rename",
|
||||
"ru": "Применить и переименовать",
|
||||
"ua": "Застосувати та перейменувати"
|
||||
},
|
||||
"IgnorePatchTuesdayErrors": {
|
||||
"en": "Ignore Patch Tuesday errors in timer (exp.)",
|
||||
@@ -708,6 +728,13 @@
|
||||
"ua": "Підтвердіть дію"
|
||||
}
|
||||
},
|
||||
"TextFieldDialog": {
|
||||
"Title": {
|
||||
"ru": "Введите значение",
|
||||
"en": "Enter value",
|
||||
"ua": "Введіть значення"
|
||||
}
|
||||
},
|
||||
"CodeBehind": {
|
||||
"MainVM": {
|
||||
"SuccessfulLogin": {
|
||||
@@ -726,12 +753,9 @@
|
||||
"ua": "Відсутній RCode"
|
||||
},
|
||||
"ConfirmRemovingAuthenticator": {
|
||||
"ru":
|
||||
"Вы точно уверены, что хотите удалить аутентификатор?\r\nПосле этого вы не сможете обмениваться, использовать торговую площадку 15 дней. Мафайл будет удалён навсегда",
|
||||
"en":
|
||||
"Are you sure you want to remove the authenticator?\r\nAfter that, you will not be able to trade or use the marketplace for 15 days. Mafile will be deleted permanently",
|
||||
"ua":
|
||||
"Ви точно впевнені, що хочете видалити автентифікатор?\r\nПісля цього ви не зможете обмінюватися, використовувати торгову площадку 15 днів. Мафайл буде видалено назавжди"
|
||||
"ru": "Вы точно уверены, что хотите удалить аутентификатор?\r\nПосле этого вы не сможете обмениваться, использовать торговую площадку 15 дней. Мафайл будет удалён навсегда",
|
||||
"en": "Are you sure you want to remove the authenticator?\r\nAfter that, you will not be able to trade or use the marketplace for 15 days. Mafile will be deleted permanently",
|
||||
"ua": "Ви точно впевнені, що хочете видалити автентифікатор?\r\nПісля цього ви не зможете обмінюватися, використовувати торгову площадку 15 днів. Мафайл буде видалено назавжди"
|
||||
|
||||
},
|
||||
"AuthenticatorRemoved": {
|
||||
@@ -800,12 +824,9 @@
|
||||
"ua": "помилки:"
|
||||
},
|
||||
"RemoveMafileConfirmation": {
|
||||
"ru":
|
||||
"Удалить mafile?\r\nМафайл будет перемещен в папку 'mafiles_removed'\r\nЭто действие НЕ удаляет Steam Guard с аккаунта",
|
||||
"en":
|
||||
"Remove mafile?\r\nMafile will be moved to 'mafiles_removed' directory\r\nThis action will NOT remove the Steam Guard from the account",
|
||||
"ua":
|
||||
"Видалити mafile?\r\nМафайл буде переміщено у каталог 'mafiles_removed'\r\nЦя дія НЕ видаляє автентифікатор з облікового запису"
|
||||
"ru": "Удалить mafile?\r\nМафайл будет перемещен в папку 'mafiles_removed'\r\nЭто действие НЕ удаляет Steam Guard с аккаунта",
|
||||
"en": "Remove mafile?\r\nMafile will be moved to 'mafiles_removed' directory\r\nThis action will NOT remove the Steam Guard from the account",
|
||||
"ua": "Видалити mafile?\r\nМафайл буде переміщено у каталог 'mafiles_removed'\r\nЦя дія НЕ видаляє автентифікатор з облікового запису"
|
||||
},
|
||||
"CantRemoveAlreadyExist": {
|
||||
"ru": "Не удалось переместить mafile, возможно в папке уже существует мафайл с таким именем.",
|
||||
@@ -859,9 +880,23 @@
|
||||
},
|
||||
"CantDecryptPassword": {
|
||||
"en": "Can't decrypt password. Maybe password from this mafile was encrypted with another encryption password",
|
||||
"ru":
|
||||
"Не удалось расшифровать пароль. Возможно пароль из этого мафайла был зашифрован другим паролем шифрования",
|
||||
"ru": "Не удалось расшифровать пароль. Возможно пароль из этого мафайла был зашифрован другим паролем шифрования",
|
||||
"ua": "Не вдалося розшифрувати пароль. Можливо пароль з цього мафайла був зашифрований іншим паролем шифрування"
|
||||
},
|
||||
"CreateGroupTitle": {
|
||||
"en": "New group",
|
||||
"ru": "Новая группа",
|
||||
"ua": "Нова група"
|
||||
},
|
||||
"CreateGroupInput": {
|
||||
"en": "Enter group name",
|
||||
"ru": "Введите имя группы",
|
||||
"ua": "Введіть назву групи"
|
||||
},
|
||||
"CreateGroupSuccess": {
|
||||
"en": "Group created",
|
||||
"ru": "Группа создана",
|
||||
"ua": "Групу створено"
|
||||
}
|
||||
},
|
||||
"ErrorTranslator": {
|
||||
@@ -1116,10 +1151,8 @@
|
||||
},
|
||||
"InvalidStateWithStatus2": {
|
||||
"ru": "Неверное состояние (InvalidState) со статусом 2. Откройте ссылку на руководство сверху этого окна.",
|
||||
"en":
|
||||
"Invalid state (InvalidState) with status 2. Open link to the troubleshooting guide at the top of this window.",
|
||||
"ua":
|
||||
"Невірний стан (InvalidState) зі статусом 2. Відкрийте посилання на посібник з усунення несправностей у верхній частині цього вікна."
|
||||
"en": "Invalid state (InvalidState) with status 2. Open link to the troubleshooting guide at the top of this window.",
|
||||
"ua": "Невірний стан (InvalidState) зі статусом 2. Відкрийте посилання на посібник з усунення несправностей у верхній частині цього вікна."
|
||||
},
|
||||
"GeneralFailure": {
|
||||
"ru": "General Failure",
|
||||
@@ -1168,8 +1201,7 @@
|
||||
"Guard": {
|
||||
"ru": "Ошибка: Был запрошен вход с помощью Steam Guard. На аккаунте уже активирован мобильный аутентификатор",
|
||||
"en": "Error: Login with Steam Guard was requested. Mobile authenticator is already activated on the account",
|
||||
"ua":
|
||||
"Помилка: Був запрошений вхід за допомогою Steam Guard. На акаунті вже активовано мобільний автентифікатор"
|
||||
"ua": "Помилка: Був запрошений вхід за допомогою Steam Guard. На акаунті вже активовано мобільний автентифікатор"
|
||||
},
|
||||
"Unknown": {
|
||||
"ru": "Ошибка: Неизвестный тип аутентификации",
|
||||
@@ -1272,12 +1304,9 @@
|
||||
"ua": "На пошту надіслано лист. Відкрийте посилання з листа, а потім натисніть — 'Продовжити'"
|
||||
},
|
||||
"ClickOnEmailLinkRetry": {
|
||||
"ru":
|
||||
"Не удалось подтвердить переход по ссылке. Убедитесь, что вы открыли ссылку из письма, затем нажмите — 'Продолжить'",
|
||||
"en":
|
||||
"We couldn’t verify that you clicked the link. Make sure you opened the link from the email, then click 'Proceed'",
|
||||
"ua":
|
||||
"Не вдалося підтвердити перехід за посиланням. Переконайтесь, що ви відкрили посилання з листа, а потім натисніть — 'Продовжити'"
|
||||
"ru": "Не удалось подтвердить переход по ссылке. Убедитесь, что вы открыли ссылку из письма, затем нажмите — 'Продолжить'",
|
||||
"en": "We couldn’t verify that you clicked the link. Make sure you opened the link from the email, then click 'Proceed'",
|
||||
"ua": "Не вдалося підтвердити перехід за посиланням. Переконайтесь, що ви відкрили посилання з листа, а потім натисніть — 'Продовжити'"
|
||||
},
|
||||
"PhoneHint": {
|
||||
"ru": "На телефон {0} была отправлена СМС",
|
||||
@@ -1285,12 +1314,9 @@
|
||||
"ua": "На телефон {0} було відправлено СМС"
|
||||
},
|
||||
"EnterPhoneNumber": {
|
||||
"ru":
|
||||
"Введите номер в международном формате, только цифры (без '+', пробелов и скобок), или оставьте поле пустым, чтобы привязать без номера",
|
||||
"en":
|
||||
"Enter number in international format, digits only (no '+', spaces or brackets), or leave the field empty to link without a number",
|
||||
"ua":
|
||||
"Введіть номер у міжнародному форматі, лише цифри (без '+', пробілів і дужок), або залиште поле порожнім, щоб прив’язати без номера"
|
||||
"ru": "Введите номер в международном формате, только цифры (без '+', пробелов и скобок), или оставьте поле пустым, чтобы привязать без номера",
|
||||
"en": "Enter number in international format, digits only (no '+', spaces or brackets), or leave the field empty to link without a number",
|
||||
"ua": "Введіть номер у міжнародному форматі, лише цифри (без '+', пробілів і дужок), або залиште поле порожнім, щоб прив’язати без номера"
|
||||
}
|
||||
},
|
||||
"MafileMoverVM": {
|
||||
@@ -1315,11 +1341,9 @@
|
||||
"ua": "RCode: {0}\r\nІм'я файлу: {1}.mafile"
|
||||
},
|
||||
"GuardIsNotActive": {
|
||||
"ru":
|
||||
"Steam Guard установлен на данном аккаунте. Чтобы привязать mafile к аккаунту, воспользуйтесь окном \"Привязать\"",
|
||||
"ru": "Steam Guard установлен на данном аккаунте. Чтобы привязать mafile к аккаунту, воспользуйтесь окном \"Привязать\"",
|
||||
"en": "Steam Guard is set on this account. To link mafile to the account, use the 'Link' window",
|
||||
"ua":
|
||||
"Steam Guard встановлено на цьому акаунті. Щоб прив'язати mafile до акаунту, скористайтеся вікном 'Прив'язати'"
|
||||
"ua": "Steam Guard встановлено на цьому акаунті. Щоб прив'язати mafile до акаунту, скористайтеся вікном 'Прив'язати'"
|
||||
},
|
||||
"SeemsNoPhoneNumber": {
|
||||
"en": "The account has no phone number linked, returned Fail code (2)",
|
||||
@@ -1354,14 +1378,44 @@
|
||||
"ua": "Авто "
|
||||
},
|
||||
"TimerPreventedOverlap": {
|
||||
"ru":
|
||||
"Авто: таймер подтверждений пытался начать новый цикл, когда предыдущий еще не завершился. Советуем увеличить задержку",
|
||||
"en":
|
||||
"Auto: confirmation timer tried to start new cycle when previous one was not finished yet. We recommend to increase delay",
|
||||
"ua":
|
||||
"Авто: таймер підтверджень намагався запустити новий цикл, коли попередній ще не закінчився. Рекомендуємо збільшити затримку"
|
||||
"ru": "Авто: таймер подтверждений пытался начать новый цикл, когда предыдущий еще не завершился. Советуем увеличить задержку",
|
||||
"en": "Auto: confirmation timer tried to start new cycle when previous one was not finished yet. We recommend to increase delay",
|
||||
"ua": "Авто: таймер підтверджень намагався запустити новий цикл, коли попередній ще не закінчився. Рекомендуємо збільшити затримку"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SettingsVM": {
|
||||
"MafileRenaming": {
|
||||
"Error": {
|
||||
"en": "Error while renaming mafiles. It was saved in log",
|
||||
"ru": "Ошибка при переименовании мафайлов. Она была сохранена в log",
|
||||
"ua": "Помилка при перейменуванні мафайлів. Вона була збережена в log"
|
||||
},
|
||||
"AllRenamed": {
|
||||
"ru": "Все мафайлы успешно переименованы ({0}).\r\nБыла создана резервная копия: {1}",
|
||||
"en": "All mafiles successfully renamed ({0}).\r\nBackup was created: {1}",
|
||||
"ua": "Всі мафайли успішно перейменовані ({0}).\r\nБуло створено резервну копію: {1}"
|
||||
},
|
||||
"PartialSuccess": {
|
||||
"en": "Total mafiles: {0}\r\nRenamed: {1}\r\nConflicts: {2}\r\nErrors: {3}\r\n\r\nBackup of mafiles saved in file: {4}",
|
||||
"ru": "Всего мафайлов: {0}\r\nПереименовано: {1}\r\nКонфликты: {2}\r\nОшибок: {3}\r\n\r\nРезервная копия мафайлов сохранена в файле: {4}",
|
||||
"ua": "Всього мафайлів: {0}\r\nПерейменовано: {1}\r\nКонфлікти: {2}\r\nПомилок: {3}\r\n\r\nРезервна копія мафайлів збережена у файлі: {4}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProxyStorage": {
|
||||
"ErrorWhileLoadingProxies": {
|
||||
"en": "Error while loading proxies",
|
||||
"ru": "Ошибка при загрузке прокси",
|
||||
"ua": "Помилка при завантаженні проксі"
|
||||
}
|
||||
},
|
||||
"Settings": {
|
||||
"ErrorWhileLoadingSettings": {
|
||||
"en": "Error while loading settings. Settings were reset",
|
||||
"ru": "Ошибка при загрузке настроек. Настройки были сброшены",
|
||||
"ua": "Помилка при завантаженні налаштувань. Налаштування були скинуті"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CantAlignTimeError": {
|
||||
"ru":
|
||||
|
||||
Reference in New Issue
Block a user